From 9dcbc195ad53d1467c5566184567407c8bcef31d Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 23 May 2021 13:56:49 -0400 Subject: Docs: Add readme for mikktspace --- intern/mikktspace/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 intern/mikktspace/README.md diff --git a/intern/mikktspace/README.md b/intern/mikktspace/README.md new file mode 100644 index 00000000000..9fda1559e44 --- /dev/null +++ b/intern/mikktspace/README.md @@ -0,0 +1,4 @@ +# MikkTSpace +A common standard for tangent space used in baking tools to produce normal maps. + +More information can be found at http://www.mikktspace.com/. -- cgit v1.2.3 From 7e841c797fb86f4d476f82d7c1cdd44263c542fd Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 23 May 2021 15:31:54 -0400 Subject: UI: Use title case for labels --- source/blender/editors/space_sequencer/sequencer_add.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index cd75bc98b15..7e76da0400c 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -158,7 +158,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) ot->prop = RNA_def_boolean(ot->srna, "set_view_transform", true, - "Set view transform", + "Set View Transform", "Set appropriate view transform based on media colorspace"); } } -- cgit v1.2.3 From 43153e2324548102ff47eb3ce97db2b59ae26ef4 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 24 May 2021 05:37:46 +0200 Subject: Fix T88466: Sound strips prevent strip rendering When sound strip is above another strip such as movie strip, it prevents from rendering movie strip. This bug was introduced in 0b7744f4da66. Function `must_render_strip()` checks if there is any strip with `SEQ_BLEND_REPLACE` blending and considers this strip as lowest strip in stack. Sound strips do have this blend mode set, which caused the bug. Remove all sound strips and muted strips from stack collection before checking with `must_render_strip()` function --- source/blender/sequencer/intern/render.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index f3cea273fdf..d881c90a1e0 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -273,15 +273,6 @@ static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibl * Order of applying these conditions is important. */ static bool must_render_strip(const Sequence *seq, SeqCollection *strips_under_playhead) { - /* Sound strips are not rendered. */ - if (seq->type == SEQ_TYPE_SOUND_RAM) { - return false; - } - /* Muted strips are not rendered. */ - if ((seq->flag & SEQ_MUTE) != 0) { - return false; - } - bool seq_have_effect_in_stack = false; Sequence *seq_iter; SEQ_ITERATOR_FOREACH (seq_iter, strips_under_playhead) { @@ -340,6 +331,15 @@ static void collection_filter_channel_up_to_incl(SeqCollection *collection, cons static void collection_filter_rendered_strips(SeqCollection *collection) { Sequence *seq; + + /* Remove sound strips and muted strips from collection, because these are not rendered. + * Function must_render_strip() don't have to check for these strips anymore. */ + SEQ_ITERATOR_FOREACH (seq, collection) { + if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) { + SEQ_collection_remove_strip(seq, collection); + } + } + SEQ_ITERATOR_FOREACH (seq, collection) { if (must_render_strip(seq, collection)) { continue; -- cgit v1.2.3 From 24004e74fa442a1b817180e62aefe5d4d526f406 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 24 May 2021 12:36:13 +0200 Subject: Fix T88524: GPencil PDF does not take into account the marker camera The camera was not checked before doing the export. --- source/blender/io/gpencil/intern/gpencil_io_base.cc | 10 +++++++--- source/blender/io/gpencil/intern/gpencil_io_base.hh | 2 +- source/blender/io/gpencil/intern/gpencil_io_capi.cc | 10 ++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc index e79a2bc98ff..a2c1b8f5af6 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc @@ -41,6 +41,7 @@ #include "BKE_gpencil_geom.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_scene.h" #include "UI_view2d.h" @@ -69,18 +70,21 @@ GpencilIO::GpencilIO(const GpencilIOParams *iparams) cfra_ = iparams->frame_cur; /* Calculate camera matrix. */ - prepare_camera_params(iparams); + prepare_camera_params(scene_, iparams); } -void GpencilIO::prepare_camera_params(const GpencilIOParams *iparams) +void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *iparams) { params_ = *iparams; const bool is_pdf = params_.mode == GP_EXPORT_TO_PDF; const bool any_camera = (params_.v3d->camera != nullptr); const bool force_camera_view = is_pdf && any_camera; + /* Ensure camera switch is applied. */ + BKE_scene_camera_switch_update(scene); + /* Calculate camera matrix. */ - Object *cam_ob = params_.v3d->camera; + Object *cam_ob = scene->camera; if (cam_ob != nullptr) { /* Set up parameters. */ CameraParams params; diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh index 2e1e1707c78..c3c6f1156bb 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh @@ -50,7 +50,7 @@ class GpencilIO { GpencilIO(const GpencilIOParams *iparams); void frame_number_set(const int value); - void prepare_camera_params(const GpencilIOParams *iparams); + void prepare_camera_params(Scene *scene, const GpencilIOParams *iparams); protected: GpencilIOParams params_; diff --git a/source/blender/io/gpencil/intern/gpencil_io_capi.cc b/source/blender/io/gpencil/intern/gpencil_io_capi.cc index 8093ec3c52d..544c51e0b4f 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_capi.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_capi.cc @@ -121,7 +121,7 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph, CFRA = i; BKE_scene_graph_update_for_newframe(depsgraph); - exporter->prepare_camera_params(iparams); + exporter->prepare_camera_params(scene, iparams); exporter->frame_number_set(i); exporter->add_newpage(); exporter->add_body(); @@ -130,10 +130,11 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph, /* Back to original frame. */ exporter->frame_number_set(iparams->frame_cur); CFRA = iparams->frame_cur; + BKE_scene_camera_switch_update(scene); BKE_scene_graph_update_for_newframe(depsgraph); } else { - exporter->prepare_camera_params(iparams); + exporter->prepare_camera_params(scene, iparams); exporter->add_newpage(); exporter->add_body(); result = exporter->write(); @@ -146,6 +147,7 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph, /* Export current frame in SVG. */ #ifdef WITH_PUGIXML static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter, + Scene *scene, const GpencilIOParams *iparams, const bool newpage, const bool body, @@ -153,7 +155,7 @@ static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter, { bool result = false; exporter->frame_number_set(iparams->frame_cur); - exporter->prepare_camera_params(iparams); + exporter->prepare_camera_params(scene, iparams); if (newpage) { result |= exporter->add_newpage(); @@ -189,7 +191,7 @@ bool gpencil_io_export(const char *filename, GpencilIOParams *iparams) #ifdef WITH_PUGIXML case GP_EXPORT_TO_SVG: { GpencilExporterSVG exporter = GpencilExporterSVG(filename, iparams); - return gpencil_io_export_frame_svg(&exporter, iparams, true, true, true); + return gpencil_io_export_frame_svg(&exporter, scene_, iparams, true, true, true); break; } #endif -- cgit v1.2.3 From 6c2c16b5f87e30a338d3f2e7259887403d736f50 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Mon, 24 May 2021 12:02:45 +0100 Subject: Nodes: move shader curves node to C++ Prepare node for conversion to Geometry Nodes. There should be no functional changes. Reviewed By: JacquesLucke, LazyDodo Differential Revision: https://developer.blender.org/D11226 --- source/blender/nodes/CMakeLists.txt | 2 +- .../nodes/shader/nodes/node_shader_curves.c | 246 --------------------- .../nodes/shader/nodes/node_shader_curves.cc | 246 +++++++++++++++++++++ 3 files changed, 247 insertions(+), 247 deletions(-) delete mode 100644 source/blender/nodes/shader/nodes/node_shader_curves.c create mode 100644 source/blender/nodes/shader/nodes/node_shader_curves.cc diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 33b56fd0de0..9d21ff19f46 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -219,7 +219,7 @@ set(SRC shader/nodes/node_shader_camera.c shader/nodes/node_shader_clamp.cc shader/nodes/node_shader_common.c - shader/nodes/node_shader_curves.c + shader/nodes/node_shader_curves.cc shader/nodes/node_shader_displacement.c shader/nodes/node_shader_eevee_specular.c shader/nodes/node_shader_emission.c diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.c b/source/blender/nodes/shader/nodes/node_shader_curves.c deleted file mode 100644 index 42299a193e2..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_curves.c +++ /dev/null @@ -1,246 +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. - */ - -/** \file - * \ingroup shdnodes - */ - -#include "node_shader_util.h" - -/* **************** CURVE VEC ******************** */ -static bNodeSocketTemplate sh_node_curve_vec_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_curve_vec_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, -}; - -static void node_shader_exec_curve_vec(void *UNUSED(data), - int UNUSED(thread), - bNode *node, - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float vec[3]; - - /* stack order input: vec */ - /* stack order output: vec */ - nodestack_get_vec(vec, SOCK_VECTOR, in[1]); - BKE_curvemapping_evaluate3F(node->storage, out[0]->vec, vec); -} - -static void node_shader_init_curve_vec(bNodeTree *UNUSED(ntree), bNode *node) -{ - node->storage = BKE_curvemapping_add(3, -1.0f, -1.0f, 1.0f, 1.0f); -} - -static int gpu_shader_curve_vec(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - float *array, layer; - int size; - - CurveMapping *cumap = node->storage; - - BKE_curvemapping_table_RGBA(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_xyz[3][4]; - float range_xyz[3]; - - for (int a = 0; a < 3; a++) { - const CurveMap *cm = &cumap->cm[a]; - ext_xyz[a][0] = cm->mintable; - ext_xyz[a][2] = cm->maxtable; - range_xyz[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_xyz[a][1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_xyz[a])) : - 1e8f; - ext_xyz[a][3] = (cm->ext_out[0] != 0.0f) ? - (cm->ext_out[1] / (cm->ext_out[0] * range_xyz[a])) : - 1e8f; - } - else { - ext_xyz[a][1] = 0.0f; - ext_xyz[a][3] = 0.0f; - } - } - - return GPU_stack_link(mat, - node, - "curves_vec", - in, - out, - tex, - GPU_constant(&layer), - GPU_uniform(range_xyz), - GPU_uniform(ext_xyz[0]), - GPU_uniform(ext_xyz[1]), - GPU_uniform(ext_xyz[2])); -} - -void register_node_type_sh_curve_vec(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_curve_vec_in, sh_node_curve_vec_out); - node_type_init(&ntype, node_shader_init_curve_vec); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); - node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_vec); - node_type_gpu(&ntype, gpu_shader_curve_vec); - - nodeRegisterType(&ntype); -} - -/* **************** CURVE RGB ******************** */ -static bNodeSocketTemplate sh_node_curve_rgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_curve_rgb_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; - -static void node_shader_exec_curve_rgb(void *UNUSED(data), - int UNUSED(thread), - bNode *node, - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float vec[3]; - float fac; - - /* stack order input: vec */ - /* stack order output: vec */ - nodestack_get_vec(&fac, SOCK_FLOAT, in[0]); - nodestack_get_vec(vec, SOCK_VECTOR, in[1]); - BKE_curvemapping_evaluateRGBF(node->storage, out[0]->vec, vec); - if (fac != 1.0f) { - interp_v3_v3v3(out[0]->vec, vec, out[0]->vec, fac); - } -} - -static void node_shader_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node) -{ - node->storage = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); -} - -static int gpu_shader_curve_rgb(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - float *array, layer; - int size; - bool use_opti = true; - - CurveMapping *cumap = node->storage; - - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_RGBA(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_rgba[4][4]; - float range_rgba[4]; - - for (int a = 0; a < CM_TOT; a++) { - const CurveMap *cm = &cumap->cm[a]; - ext_rgba[a][0] = cm->mintable; - ext_rgba[a][2] = cm->maxtable; - range_rgba[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_rgba[a][1] = (cm->ext_in[0] != 0.0f) ? - (cm->ext_in[1] / (cm->ext_in[0] * range_rgba[a])) : - 1e8f; - ext_rgba[a][3] = (cm->ext_out[0] != 0.0f) ? - (cm->ext_out[1] / (cm->ext_out[0] * range_rgba[a])) : - 1e8f; - } - else { - ext_rgba[a][1] = 0.0f; - ext_rgba[a][3] = 0.0f; - } - - /* Check if rgb comps are just linear. */ - if (a < 3) { - if (range_rgba[a] != 1.0f || ext_rgba[a][1] != 1.0f || ext_rgba[a][2] != 1.0f || - cm->totpoint != 2 || cm->curve[0].x != 0.0f || cm->curve[0].y != 0.0f || - cm->curve[1].x != 1.0f || cm->curve[1].y != 1.0f) { - use_opti = false; - } - } - } - - if (use_opti) { - return GPU_stack_link(mat, - node, - "curves_rgb_opti", - in, - out, - tex, - GPU_constant(&layer), - GPU_uniform(range_rgba), - GPU_uniform(ext_rgba[3])); - } - - return GPU_stack_link(mat, - node, - "curves_rgb", - in, - out, - tex, - GPU_constant(&layer), - GPU_uniform(range_rgba), - GPU_uniform(ext_rgba[0]), - GPU_uniform(ext_rgba[1]), - GPU_uniform(ext_rgba[2]), - GPU_uniform(ext_rgba[3])); -} - -void register_node_type_sh_curve_rgb(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_curve_rgb_in, sh_node_curve_rgb_out); - node_type_init(&ntype, node_shader_init_curve_rgb); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); - node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_rgb); - node_type_gpu(&ntype, gpu_shader_curve_rgb); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc new file mode 100644 index 00000000000..ff619306682 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -0,0 +1,246 @@ +/* + * 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. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.h" + +/* **************** CURVE VEC ******************** */ +static bNodeSocketTemplate sh_node_curve_vec_in[] = { + {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE}, + {-1, ""}, +}; + +static bNodeSocketTemplate sh_node_curve_vec_out[] = { + {SOCK_VECTOR, N_("Vector")}, + {-1, ""}, +}; + +static void node_shader_exec_curve_vec(void *UNUSED(data), + int UNUSED(thread), + bNode *node, + bNodeExecData *UNUSED(execdata), + bNodeStack **in, + bNodeStack **out) +{ + float vec[3]; + + /* stack order input: vec */ + /* stack order output: vec */ + nodestack_get_vec(vec, SOCK_VECTOR, in[1]); + BKE_curvemapping_evaluate3F((CurveMapping *)node->storage, out[0]->vec, vec); +} + +static void node_shader_init_curve_vec(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->storage = BKE_curvemapping_add(3, -1.0f, -1.0f, 1.0f, 1.0f); +} + +static int gpu_shader_curve_vec(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + float *array, layer; + int size; + + CurveMapping *cumap = (CurveMapping *)node->storage; + + BKE_curvemapping_table_RGBA(cumap, &array, &size); + GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); + + float ext_xyz[3][4]; + float range_xyz[3]; + + for (int a = 0; a < 3; a++) { + const CurveMap *cm = &cumap->cm[a]; + ext_xyz[a][0] = cm->mintable; + ext_xyz[a][2] = cm->maxtable; + range_xyz[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); + /* Compute extrapolation gradients. */ + if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { + ext_xyz[a][1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_xyz[a])) : + 1e8f; + ext_xyz[a][3] = (cm->ext_out[0] != 0.0f) ? + (cm->ext_out[1] / (cm->ext_out[0] * range_xyz[a])) : + 1e8f; + } + else { + ext_xyz[a][1] = 0.0f; + ext_xyz[a][3] = 0.0f; + } + } + + return GPU_stack_link(mat, + node, + "curves_vec", + in, + out, + tex, + GPU_constant(&layer), + GPU_uniform(range_xyz), + GPU_uniform(ext_xyz[0]), + GPU_uniform(ext_xyz[1]), + GPU_uniform(ext_xyz[2])); +} + +void register_node_type_sh_curve_vec(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); + node_type_socket_templates(&ntype, sh_node_curve_vec_in, sh_node_curve_vec_out); + node_type_init(&ntype, node_shader_init_curve_vec); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_vec); + node_type_gpu(&ntype, gpu_shader_curve_vec); + + nodeRegisterType(&ntype); +} + +/* **************** CURVE RGB ******************** */ +static bNodeSocketTemplate sh_node_curve_rgb_in[] = { + {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_FACTOR}, + {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f}, + {-1, ""}, +}; + +static bNodeSocketTemplate sh_node_curve_rgb_out[] = { + {SOCK_RGBA, N_("Color")}, + {-1, ""}, +}; + +static void node_shader_exec_curve_rgb(void *UNUSED(data), + int UNUSED(thread), + bNode *node, + bNodeExecData *UNUSED(execdata), + bNodeStack **in, + bNodeStack **out) +{ + float vec[3]; + float fac; + + /* stack order input: vec */ + /* stack order output: vec */ + nodestack_get_vec(&fac, SOCK_FLOAT, in[0]); + nodestack_get_vec(vec, SOCK_VECTOR, in[1]); + BKE_curvemapping_evaluateRGBF((CurveMapping *)node->storage, out[0]->vec, vec); + if (fac != 1.0f) { + interp_v3_v3v3(out[0]->vec, vec, out[0]->vec, fac); + } +} + +static void node_shader_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->storage = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); +} + +static int gpu_shader_curve_rgb(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + float *array, layer; + int size; + bool use_opti = true; + + CurveMapping *cumap = (CurveMapping *)node->storage; + + BKE_curvemapping_init(cumap); + BKE_curvemapping_table_RGBA(cumap, &array, &size); + GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); + + float ext_rgba[4][4]; + float range_rgba[4]; + + for (int a = 0; a < CM_TOT; a++) { + const CurveMap *cm = &cumap->cm[a]; + ext_rgba[a][0] = cm->mintable; + ext_rgba[a][2] = cm->maxtable; + range_rgba[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); + /* Compute extrapolation gradients. */ + if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { + ext_rgba[a][1] = (cm->ext_in[0] != 0.0f) ? + (cm->ext_in[1] / (cm->ext_in[0] * range_rgba[a])) : + 1e8f; + ext_rgba[a][3] = (cm->ext_out[0] != 0.0f) ? + (cm->ext_out[1] / (cm->ext_out[0] * range_rgba[a])) : + 1e8f; + } + else { + ext_rgba[a][1] = 0.0f; + ext_rgba[a][3] = 0.0f; + } + + /* Check if rgb comps are just linear. */ + if (a < 3) { + if (range_rgba[a] != 1.0f || ext_rgba[a][1] != 1.0f || ext_rgba[a][2] != 1.0f || + cm->totpoint != 2 || cm->curve[0].x != 0.0f || cm->curve[0].y != 0.0f || + cm->curve[1].x != 1.0f || cm->curve[1].y != 1.0f) { + use_opti = false; + } + } + } + + if (use_opti) { + return GPU_stack_link(mat, + node, + "curves_rgb_opti", + in, + out, + tex, + GPU_constant(&layer), + GPU_uniform(range_rgba), + GPU_uniform(ext_rgba[3])); + } + + return GPU_stack_link(mat, + node, + "curves_rgb", + in, + out, + tex, + GPU_constant(&layer), + GPU_uniform(range_rgba), + GPU_uniform(ext_rgba[0]), + GPU_uniform(ext_rgba[1]), + GPU_uniform(ext_rgba[2]), + GPU_uniform(ext_rgba[3])); +} + +void register_node_type_sh_curve_rgb(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); + node_type_socket_templates(&ntype, sh_node_curve_rgb_in, sh_node_curve_rgb_out); + node_type_init(&ntype, node_shader_init_curve_rgb); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_rgb); + node_type_gpu(&ntype, gpu_shader_curve_rgb); + + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From 1318cb26fe07e221c8bc539f21a626f74de354d1 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 24 May 2021 14:31:17 +0200 Subject: Cleanup: Refactor PlaneTrack and PlaneDistort operations Deduplicates code by introducing a PlaneDirtortBaseOperation for common logic. Reviewed By: #compositing, jbakker Differential Revision: https://developer.blender.org/D11273 --- .../operations/COM_PlaneDistortCommonOperation.cc | 68 +++++++++------------- .../operations/COM_PlaneDistortCommonOperation.h | 58 +++++++++--------- .../operations/COM_PlaneTrackOperation.cc | 53 +++++++---------- .../operations/COM_PlaneTrackOperation.h | 5 +- 4 files changed, 81 insertions(+), 103 deletions(-) diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index 46ae00dee34..4edcc206f5b 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -31,6 +31,31 @@ namespace blender::compositor { +PlaneDistortBaseOperation::PlaneDistortBaseOperation() + : m_motion_blur_samples(1), m_motion_blur_shutter(0.5f) +{ +} + +void PlaneDistortBaseOperation::calculateCorners(const float corners[4][2], + bool normalized, + int sample) +{ + BLI_assert(sample < this->m_motion_blur_samples); + MotionSample *sample_data = &this->m_samples[sample]; + if (normalized) { + for (int i = 0; i < 4; i++) { + sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); + sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); + } + } + else { + for (int i = 0; i < 4; i++) { + sample_data->frameSpaceCorners[i][0] = corners[i][0]; + sample_data->frameSpaceCorners[i][1] = corners[i][1]; + } + } +} + /* ******** PlaneDistort WarpImage ******** */ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], float deriv[2][2]) @@ -46,13 +71,11 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo deriv[1][1] = (matrix[1][1] - matrix[1][2] * uv[1]) / vec[2]; } -PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() +PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation() { this->addInputSocket(DataType::Color, ResizeMode::None); this->addOutputSocket(DataType::Color); this->m_pixelReader = nullptr; - this->m_motion_blur_samples = 1; - this->m_motion_blur_shutter = 0.5f; this->flags.complex = true; } @@ -60,24 +83,13 @@ void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], bool normalized, int sample) { - BLI_assert(sample < this->m_motion_blur_samples); + PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample); + const int width = this->m_pixelReader->getWidth(); const int height = this->m_pixelReader->getHeight(); float frame_corners[4][2] = { {0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}}; MotionSample *sample_data = &this->m_samples[sample]; - if (normalized) { - for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); - sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); - } - } - else { - for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0]; - sample_data->frameSpaceCorners[i][1] = corners[i][1]; - } - } BKE_tracking_homography_between_two_quads( sample_data->frameSpaceCorners, frame_corners, sample_data->perspectiveMatrix); } @@ -147,34 +159,12 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( /* ******** PlaneDistort Mask ******** */ -PlaneDistortMaskOperation::PlaneDistortMaskOperation() +PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation() { addOutputSocket(DataType::Value); /* Currently hardcoded to 8 samples. */ m_osa = 8; - this->m_motion_blur_samples = 1; - this->m_motion_blur_shutter = 0.5f; -} - -void PlaneDistortMaskOperation::calculateCorners(const float corners[4][2], - bool normalized, - int sample) -{ - BLI_assert(sample < this->m_motion_blur_samples); - MotionSample *sample_data = &this->m_samples[sample]; - if (normalized) { - for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); - sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); - } - } - else { - for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0]; - sample_data->frameSpaceCorners[i][1] = corners[i][1]; - } - } } void PlaneDistortMaskOperation::initExecution() diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index 95e5c86bd4d..cc6e4d00d71 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -32,21 +32,43 @@ namespace blender::compositor { #define PLANE_DISTORT_MAX_SAMPLES 64 -class PlaneDistortWarpImageOperation : public NodeOperation { +class PlaneDistortBaseOperation : public NodeOperation { protected: struct MotionSample { float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ float perspectiveMatrix[3][3]; }; - SocketReader *m_pixelReader; MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES]; int m_motion_blur_samples; float m_motion_blur_shutter; + public: + PlaneDistortBaseOperation(); + + void setMotionBlurSamples(int samples) + { + BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); + this->m_motion_blur_samples = samples; + } + void setMotionBlurShutter(float shutter) + { + this->m_motion_blur_shutter = shutter; + } + + virtual void calculateCorners(const float corners[4][2], bool normalized, int sample); + + private: + friend class PlaneTrackCommon; +}; + +class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation { + protected: + SocketReader *m_pixelReader; + public: PlaneDistortWarpImageOperation(); - void calculateCorners(const float corners[4][2], bool normalized, int sample); + void calculateCorners(const float corners[4][2], bool normalized, int sample) override; void initExecution() override; void deinitExecution() override; @@ -56,47 +78,19 @@ class PlaneDistortWarpImageOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; - - void setMotionBlurSamples(int samples) - { - BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); - this->m_motion_blur_samples = samples; - } - void setMotionBlurShutter(float shutter) - { - this->m_motion_blur_shutter = shutter; - } }; -class PlaneDistortMaskOperation : public NodeOperation { +class PlaneDistortMaskOperation : public PlaneDistortBaseOperation { protected: - struct MotionSample { - float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ - }; int m_osa; - MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES]; float m_jitter[32][2]; - int m_motion_blur_samples; - float m_motion_blur_shutter; public: PlaneDistortMaskOperation(); - void calculateCorners(const float corners[4][2], bool normalized, int sample); - void initExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - - void setMotionBlurSamples(int samples) - { - BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); - this->m_motion_blur_samples = samples; - } - void setMotionBlurShutter(float shutter) - { - this->m_motion_blur_shutter = shutter; - } }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index 565bde6c945..0884f2ad979 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -41,6 +41,26 @@ PlaneTrackCommon::PlaneTrackCommon() this->m_planeTrackName[0] = '\0'; } +void PlaneTrackCommon::read_and_calculate_corners(PlaneDistortBaseOperation *distort_op) +{ + float corners[4][2]; + if (distort_op->m_motion_blur_samples == 1) { + readCornersFromTrack(corners, this->m_framenumber); + distort_op->calculateCorners(corners, true, 0); + } + else { + const float frame = (float)this->m_framenumber - distort_op->m_motion_blur_shutter; + const float frame_step = (distort_op->m_motion_blur_shutter * 2.0f) / + distort_op->m_motion_blur_samples; + float frame_iter = frame; + for (int sample = 0; sample < distort_op->m_motion_blur_samples; sample++) { + readCornersFromTrack(corners, frame_iter); + distort_op->calculateCorners(corners, true, sample); + frame_iter += frame_step; + } + } +} + void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) { MovieTracking *tracking; @@ -84,21 +104,7 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2], void PlaneTrackMaskOperation::initExecution() { PlaneDistortMaskOperation::initExecution(); - float corners[4][2]; - if (this->m_motion_blur_samples == 1) { - readCornersFromTrack(corners, this->m_framenumber); - calculateCorners(corners, true, 0); - } - else { - const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter; - const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples; - float frame_iter = frame; - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { - readCornersFromTrack(corners, frame_iter); - calculateCorners(corners, true, sample); - frame_iter += frame_step; - } - } + PlaneTrackCommon::read_and_calculate_corners(this); } /* ******** PlaneTrackWarpImageOperation ******** */ @@ -106,22 +112,7 @@ void PlaneTrackMaskOperation::initExecution() void PlaneTrackWarpImageOperation::initExecution() { PlaneDistortWarpImageOperation::initExecution(); - /* TODO(sergey): De-duplicate with mask operation. */ - float corners[4][2]; - if (this->m_motion_blur_samples == 1) { - readCornersFromTrack(corners, this->m_framenumber); - calculateCorners(corners, true, 0); - } - else { - const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter; - const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples; - float frame_iter = frame; - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { - readCornersFromTrack(corners, frame_iter); - calculateCorners(corners, true, sample); - frame_iter += frame_step; - } - } + PlaneTrackCommon::read_and_calculate_corners(this); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index 95a7d536742..d240c8b06e9 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -40,7 +40,7 @@ class PlaneTrackCommon { /* note: this class is not an operation itself (to prevent virtual inheritance issues) * implementation classes must make wrappers to use these methods, see below. */ - void readCornersFromTrack(float corners[4][2], float frame); + void read_and_calculate_corners(PlaneDistortBaseOperation *distort_op); void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); public: @@ -62,6 +62,9 @@ class PlaneTrackCommon { { this->m_framenumber = framenumber; } + + private: + void readCornersFromTrack(float corners[4][2], float frame); }; class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTrackCommon { -- cgit v1.2.3 From 197fa644eea0556b918e82df8740f605aef7789d Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 24 May 2021 11:35:33 -0300 Subject: Cleanup: Rename transform enum items Make them more descriptive. --- source/blender/editors/transform/transform.c | 11 ++++++----- source/blender/editors/transform/transform.h | 4 ++-- source/blender/editors/transform/transform_constraints.c | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 6ef2eaf67d6..fb3b2aa790b 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -814,7 +814,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } else if (event->type == MOUSEMOVE) { - if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) { + if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) { t->con.mode |= CON_SELECT; } @@ -1062,10 +1062,10 @@ int transformEvent(TransInfo *t, const wmEvent *event) t->state = TRANS_CONFIRM; } else if ((t->flag & T_NO_CONSTRAINT) == 0) { - if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) { + if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) { /* Confirm. */ postSelectConstraint(t); - t->modifiers &= ~(MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE); + t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE); } else { if (t->options & CTX_CAMERA) { @@ -1079,8 +1079,9 @@ int transformEvent(TransInfo *t, const wmEvent *event) } } else { - t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ? MOD_CONSTRAINT_SELECT : - MOD_CONSTRAINT_PLANE; + t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ? + MOD_CONSTRAINT_SELECT_AXIS : + MOD_CONSTRAINT_SELECT_PLANE; if (t->con.mode & CON_APPLY) { stopConstraint(t); } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 673ceba7a65..1fffeb65f44 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -154,11 +154,11 @@ typedef enum { /** #TransInfo.modifiers */ typedef enum { - MOD_CONSTRAINT_SELECT = 1 << 0, + MOD_CONSTRAINT_SELECT_AXIS = 1 << 0, MOD_PRECISION = 1 << 1, MOD_SNAP = 1 << 2, MOD_SNAP_INVERT = 1 << 3, - MOD_CONSTRAINT_PLANE = 1 << 4, + MOD_CONSTRAINT_SELECT_PLANE = 1 << 4, } eTModifier; /** #TransSnap.status */ diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 615467932a7..7d04086e20d 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -1062,7 +1062,7 @@ static void setNearestAxis3d(TransInfo *t) } if (len[0] <= len[1] && len[0] <= len[2]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { + if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) { t->con.mode |= (CON_AXIS1 | CON_AXIS2); BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s X axis"), t->spacename); } @@ -1072,7 +1072,7 @@ static void setNearestAxis3d(TransInfo *t) } } else if (len[1] <= len[0] && len[1] <= len[2]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { + if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) { t->con.mode |= (CON_AXIS0 | CON_AXIS2); BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Y axis"), t->spacename); } @@ -1082,7 +1082,7 @@ static void setNearestAxis3d(TransInfo *t) } } else if (len[2] <= len[1] && len[2] <= len[0]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { + if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) { t->con.mode |= (CON_AXIS0 | CON_AXIS1); BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Z axis"), t->spacename); } -- cgit v1.2.3 From 2265104f0b14fa0d6693d881d7e8c1b8c2aa755b Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 24 May 2021 11:30:44 -0300 Subject: Fix (unreported): Automatic transform contraint not being disabled It does not make sense to maintain automatic constraint selection when you manually set the constraint. --- source/blender/editors/transform/transform.c | 145 +++++++++++---------- .../editors/transform/transform_constraints.c | 1 - 2 files changed, 78 insertions(+), 68 deletions(-) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index fb3b2aa790b..5fc27a8b434 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -724,81 +724,92 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) static bool transform_event_modal_constraint(TransInfo *t, short modal_type) { - if (!(t->flag & T_NO_CONSTRAINT)) { - if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) { + if (t->flag & T_NO_CONSTRAINT) { + return false; + } + + if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) { + return false; + } + + int constraint_curr = -1; + + if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) { + t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE); + + /* Avoid changing orientation in this case. */ + constraint_curr = -2; + } + else if (t->con.mode & CON_APPLY) { + constraint_curr = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2); + } + + int constraint_new; + const char *msg_2d = "", *msg_3d = ""; + + /* Initialize */ + switch (modal_type) { + case TFM_MODAL_AXIS_X: + msg_2d = TIP_("along X"); + msg_3d = TIP_("along %s X"); + constraint_new = CON_AXIS0; + break; + case TFM_MODAL_AXIS_Y: + msg_2d = TIP_("along Y"); + msg_3d = TIP_("along %s Y"); + constraint_new = CON_AXIS1; + break; + case TFM_MODAL_AXIS_Z: + msg_2d = TIP_("along Z"); + msg_3d = TIP_("along %s Z"); + constraint_new = CON_AXIS2; + break; + case TFM_MODAL_PLANE_X: + msg_3d = TIP_("locking %s X"); + constraint_new = CON_AXIS1 | CON_AXIS2; + break; + case TFM_MODAL_PLANE_Y: + msg_3d = TIP_("locking %s Y"); + constraint_new = CON_AXIS0 | CON_AXIS2; + break; + case TFM_MODAL_PLANE_Z: + msg_3d = TIP_("locking %s Z"); + constraint_new = CON_AXIS0 | CON_AXIS1; + break; + default: + /* Invalid key */ return false; - } - int constraint_curr = (t->con.mode & CON_APPLY) ? - t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2) : - -1; - int constraint_new; - const char *msg_2d = "", *msg_3d = ""; + } - /* Initialize */ - switch (modal_type) { - case TFM_MODAL_AXIS_X: - msg_2d = TIP_("along X"); - msg_3d = TIP_("along %s X"); - constraint_new = CON_AXIS0; - break; - case TFM_MODAL_AXIS_Y: - msg_2d = TIP_("along Y"); - msg_3d = TIP_("along %s Y"); - constraint_new = CON_AXIS1; - break; - case TFM_MODAL_AXIS_Z: - msg_2d = TIP_("along Z"); - msg_3d = TIP_("along %s Z"); - constraint_new = CON_AXIS2; - break; - case TFM_MODAL_PLANE_X: - msg_3d = TIP_("locking %s X"); - constraint_new = CON_AXIS1 | CON_AXIS2; - break; - case TFM_MODAL_PLANE_Y: - msg_3d = TIP_("locking %s Y"); - constraint_new = CON_AXIS0 | CON_AXIS2; - break; - case TFM_MODAL_PLANE_Z: - msg_3d = TIP_("locking %s Z"); - constraint_new = CON_AXIS0 | CON_AXIS1; - break; - default: - /* Invalid key */ - return false; + if (t->flag & T_2D_EDIT) { + BLI_assert(modal_type < TFM_MODAL_PLANE_X); + if (constraint_new == CON_AXIS2) { + return false; } - - if (t->flag & T_2D_EDIT) { - BLI_assert(modal_type < TFM_MODAL_PLANE_X); - if (constraint_new == CON_AXIS2) { - return false; - } - if (constraint_curr == constraint_new) { - stopConstraint(t); - } - else { - setUserConstraint(t, constraint_new, msg_2d); - } + if (constraint_curr == constraint_new) { + stopConstraint(t); } else { - short orient_index = 1; - if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) { - /* Successive presses on existing axis, cycle orientation modes. */ - orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient)); - } + setUserConstraint(t, constraint_new, msg_2d); + } + } + else { + short orient_index = 1; + if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) { + /* Successive presses on existing axis, cycle orientation modes. */ + orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient)); + } - transform_orientations_current_set(t, orient_index); - if (orient_index == 0) { - stopConstraint(t); - } - else { - setUserConstraint(t, constraint_new, msg_3d); - } + transform_orientations_current_set(t, orient_index); + if (orient_index == 0) { + stopConstraint(t); + } + else { + setUserConstraint(t, constraint_new, msg_3d); } - t->redraw |= TREDRAW_HARD; - return true; } - return false; + t->redraw |= TREDRAW_HARD; + return true; } int transformEvent(TransInfo *t, const wmEvent *event) diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 7d04086e20d..8c74d5349ba 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -975,7 +975,6 @@ void initSelectConstraint(TransInfo *t) } setUserConstraint(t, CON_APPLY | CON_SELECT, "%s"); - setNearestAxis(t); } void selectConstraint(TransInfo *t) -- cgit v1.2.3 From 1a69d491e57cb7b7a834f265f59df3bd8f01e55b Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 24 May 2021 12:05:59 -0300 Subject: Fix T88478: Fallback to dolly not working when moving the camera in camera view This fallback is an old hack. It is difficult to have an orientation convention when several random factors determine which one should be used. In this case, to "fix" the problem, a new behavior had to be implemented. Now the redo when moving the camera in `Camera View` has the default orientation as `View`. --- source/blender/editors/transform/transform_mode_translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 0bef6364214..175b7b52a1a 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -471,6 +471,7 @@ void initTranslation(TransInfo *t) t->num.unit_type[2] = B_UNIT_NONE; } - transform_mode_default_modal_orientation_set(t, V3D_ORIENT_GLOBAL); + transform_mode_default_modal_orientation_set( + t, (t->options & CTX_CAMERA) ? V3D_ORIENT_VIEW : V3D_ORIENT_GLOBAL); } /** \} */ -- cgit v1.2.3 From 0606a37e1a8c1e9c1abe94c19848382278ed966e Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Mon, 24 May 2021 11:42:30 -0700 Subject: Fix T88500: Constrain window top position (Win32) Do not allow a window to be created that has a top position that can obscure all or part of title bar. Right and Left edges can still be specified slightly outside of monitor bounds, but top edge must be clamped to monitor top. see D11371 for more details. https://developer.blender.org/D11371 Reviewed by Ray Molenkamp --- intern/ghost/intern/GHOST_WindowWin32.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index bf142239606..3af92153e8b 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -121,19 +121,21 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, monitor.dwFlags = 0; GetMonitorInfo(MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST), &monitor); - /* Constrain size to fit within this monitor. */ + /* Constrain requested size and position to fit within this monitor. */ width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect.right - win_rect.left); height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect.bottom - win_rect.top); - win_rect.left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width); win_rect.right = win_rect.left + width; win_rect.top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height); win_rect.bottom = win_rect.top + height; - /* Adjust our requested values to allow for caption, borders, shadows, etc. - Windows API Note: You cannot specify WS_OVERLAPPED when calling. */ + /* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be + * correctly outside of monitor bounds. Note: You cannot specify WS_OVERLAPPED when calling. */ AdjustWindowRectEx(&win_rect, style & ~WS_OVERLAPPED, FALSE, extended_style); + /* But never allow a top position that can hide part of the title bar. */ + win_rect.top = max(monitor.rcWork.top, win_rect.top); + m_hWnd = ::CreateWindowExW(extended_style, // window extended style s_windowClassName, // pointer to registered class name title_16, // pointer to window name -- cgit v1.2.3 From 8c7766dc03d6beb68d81a8ca357a5b3638d16f7d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 25 May 2021 18:25:44 +1000 Subject: Cleanup: clang-format --- source/blender/editors/interface/interface_eyedropper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index e4f502950ab..b52bfc81b7a 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -138,8 +138,8 @@ void eyedropper_draw_cursor_text_region(const struct bContext *C, } const int mval[2] = { - x - region->winrct.xmin, - y - region->winrct.ymin, + x - region->winrct.xmin, + y - region->winrct.ymin, }; eyedropper_draw_cursor_text_ex(mval[0], mval[1], name); -- cgit v1.2.3 From ad447705c03ec5a1565e1b6d3618a62ecfd74792 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 25 May 2021 18:25:55 +1000 Subject: Cleanup: spelling --- source/blender/blenkernel/BKE_node.h | 2 +- .../depsgraph/intern/builder/deg_builder_nodes.cc | 4 ++-- .../modifiers/intern/MOD_nodes_evaluator.cc | 28 +++++++++++----------- source/blender/nodes/NOD_geometry_exec.hh | 6 ++--- .../geometry/nodes/node_geo_point_separate.cc | 2 +- .../nodes/geometry/nodes/node_geo_switch.cc | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 6bebf74824d..448f4ae48ad 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -325,7 +325,7 @@ typedef struct bNodeType { /* Execute a geometry node. */ NodeGeometryExecFunction geometry_node_execute; - bool geometry_node_execute_supports_lazyness; + bool geometry_node_execute_supports_laziness; /* RNA integration */ ExtensionRNA rna_ext; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 77661b4870a..ae530cc010e 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -427,9 +427,9 @@ static int foreach_id_cow_detect_need_for_update_callback(LibraryIDLinkCallbackD * * NOTE: Currently the only ID types that depsgraph may decide to not evaluate/generate COW * copies for, even though they are referenced by other data-blocks, are Collections and Objects - * (through their various visbility flags, and the ones from LayerCollections too). However, this + * (through their various visibility flags, and the ones from LayerCollections too). However, this * code is kept generic as it makes it more future-proof, and optimization here would give - * neglectable performance improvements in typical cases. + * negligible performance improvements in typical cases. * * NOTE: This mechanism may also 'fix' some missing update tagging from non-depsgraph code in * some cases. This is slightly unfortunate (as it may hide issues in other parts of Blender diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index ac5249b40a2..8c000a536d5 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -175,7 +175,7 @@ enum class NodeScheduleState { struct NodeState { /** - * Needs to be locked when any data in this state is accessed that is not explicitely marked as + * Needs to be locked when any data in this state is accessed that is not explicitly marked as * otherwise. */ std::mutex mutex; @@ -192,12 +192,12 @@ struct NodeState { MutableSpan outputs; /** - * Nodes that don't support lazyness have some special handling the first time they are executed. + * Nodes that don't support laziness have some special handling the first time they are executed. */ bool non_lazy_node_is_initialized = false; /** - * Used to check that nodes that don't support lazyness do not run more than once. + * Used to check that nodes that don't support laziness do not run more than once. */ bool has_been_executed = false; @@ -309,9 +309,9 @@ static const CPPType *get_socket_cpp_type(const SocketRef &socket) return nodes::socket_cpp_type_get(*socket.typeinfo()); } -static bool node_supports_lazyness(const DNode node) +static bool node_supports_laziness(const DNode node) { - return node->typeinfo()->geometry_node_execute_supports_lazyness; + return node->typeinfo()->geometry_node_execute_supports_laziness; } /** Implements the callbacks that might be called when a node is executed. */ @@ -644,10 +644,10 @@ class GeometryNodesEvaluator { if (!this->prepare_node_outputs_for_execution(locked_node)) { return false; } - /* Initialize nodes that don't support lazyness. This is done after at least one output is + /* Initialize nodes that don't support laziness. This is done after at least one output is * required and before we check that all required inputs are provided. This reduces the * number of "round-trips" through the task pool by one for most nodes. */ - if (!node_state.non_lazy_node_is_initialized && !node_supports_lazyness(node)) { + if (!node_state.non_lazy_node_is_initialized && !node_supports_laziness(node)) { this->initialize_non_lazy_node(locked_node); node_state.non_lazy_node_is_initialized = true; } @@ -722,7 +722,7 @@ class GeometryNodesEvaluator { /* Ignore unavailable/non-data sockets. */ continue; } - /* Nodes that don't support lazyness require all inputs. */ + /* Nodes that don't support laziness require all inputs. */ const DInputSocket input_socket = locked_node.node.input(i); this->set_input_required(locked_node, input_socket); } @@ -789,8 +789,8 @@ class GeometryNodesEvaluator { const bNode &bnode = *node->bnode(); if (node_state.has_been_executed) { - if (!node_supports_lazyness(node)) { - /* Nodes that don't support lazyness must not be executed more than once. */ + if (!node_supports_laziness(node)) { + /* Nodes that don't support laziness must not be executed more than once. */ BLI_assert_unreachable(); } } @@ -923,12 +923,12 @@ class GeometryNodesEvaluator { return; } - const bool supports_lazyness = node_supports_lazyness(locked_node.node); + const bool supports_laziness = node_supports_laziness(locked_node.node); /* Iterating over sockets instead of the states directly, because that makes it easier to * figure out which socket is missing when one of the asserts is hit. */ for (const OutputSocketRef *socket_ref : locked_node.node->outputs()) { OutputState &output_state = locked_node.node_state.outputs[socket_ref->index()]; - if (supports_lazyness) { + if (supports_laziness) { /* Expected that at least all required sockets have been computed. If more outputs become * required later, the node will be executed again. */ if (output_state.output_usage_for_execution == ValueUsage::Required) { @@ -1511,7 +1511,7 @@ void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value) bool NodeParamsProvider::lazy_require_input(StringRef identifier) { - BLI_assert(node_supports_lazyness(this->dnode)); + BLI_assert(node_supports_laziness(this->dnode)); const DInputSocket socket = get_input_by_identifier(this->dnode, identifier); BLI_assert(socket); @@ -1547,7 +1547,7 @@ bool NodeParamsProvider::output_is_required(StringRef identifier) const bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const { - BLI_assert(node_supports_lazyness(this->dnode)); + BLI_assert(node_supports_laziness(this->dnode)); const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier); BLI_assert(socket); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 52d7e097f0d..7b176b2f395 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -200,7 +200,7 @@ class GeoNodeExecParams { /** * Returns true when the output has to be computed. - * Nodes that support lazyness could use the #lazy_output_is_required variant to possibly avoid + * Nodes that support laziness could use the #lazy_output_is_required variant to possibly avoid * some computations. */ bool output_is_required(StringRef identifier) const @@ -212,7 +212,7 @@ class GeoNodeExecParams { * Tell the evaluator that a specific input is required. * This returns true when the input will only be available in the next execution. * False is returned if the input is available already. - * This can only be used when the node supports lazyness. + * This can only be used when the node supports laziness. */ bool lazy_require_input(StringRef identifier) { @@ -222,7 +222,7 @@ class GeoNodeExecParams { /** * Asks the evaluator if a specific output is required right now. If this returns false, the * value might still need to be computed later. - * This can only be used when the node supports lazyness. + * This can only be used when the node supports laziness. */ bool lazy_output_is_required(StringRef identifier) { 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 d2b024b208c..ff7e95e0221 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -167,6 +167,6 @@ void register_node_type_geo_point_separate() geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out); ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec; - ntype.geometry_node_execute_supports_lazyness = true; + ntype.geometry_node_execute_supports_laziness = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index bb0a20f4561..e0daae0c172 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -171,7 +171,7 @@ void register_node_type_geo_switch() node_type_update(&ntype, blender::nodes::geo_node_switch_update); node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec; - ntype.geometry_node_execute_supports_lazyness = true; + ntype.geometry_node_execute_supports_laziness = true; ntype.draw_buttons = geo_node_switch_layout; nodeRegisterType(&ntype); } -- cgit v1.2.3 From e3faef686d3850f2e87d1a92506ba487a3594242 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 25 May 2021 10:38:23 +0200 Subject: Fix/Refactor RNA ID preview getter creating preview data. Same as with forcefields, accessors should never generate data. --- .../modules/bl_previews_utils/bl_previews_render.py | 14 +++++++++----- source/blender/makesrna/intern/rna_ID.c | 21 ++++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py index 979b47f7a14..51ea53c0eba 100644 --- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py +++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py @@ -37,6 +37,9 @@ OBJECT_TYPES_RENDER = {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT'} def ids_nolib(bids): return (bid for bid in bids if not bid.library) +def ids_nolib_with_preview(bids): + return (bid for bid in bids if (not bid.library and bid.preview)) + def rna_backup_gen(data, include_props=None, exclude_props=None, root=()): # only writable properties... @@ -313,8 +316,9 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern): image = bpy.data.images[render_context.image, None] item = getattr(bpy.data, item_container)[item_name, None] image.reload() - item.preview.image_size = (RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE) - item.preview.image_pixels_float[:] = image.pixels + preview = item.preview_ensure() + preview.image_size = (RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE) + preview.image_pixels_float[:] = image.pixels # And now, main code! do_save = True @@ -451,15 +455,15 @@ def do_clear_previews(do_objects, do_collections, do_scenes, do_data_intern): bpy.ops.wm.previews_clear(id_type={'SHADING'}) if do_objects: - for ob in ids_nolib(bpy.data.objects): + for ob in ids_nolib_with_preview(bpy.data.objects): ob.preview.image_size = (0, 0) if do_collections: - for grp in ids_nolib(bpy.data.collections): + for grp in ids_nolib_with_preview(bpy.data.collections): grp.preview.image_size = (0, 0) if do_scenes: - for scene in ids_nolib(bpy.data.scenes): + for scene in ids_nolib_with_preview(bpy.data.scenes): scene.preview.image_size = (0, 0) print("Saving %s..." % bpy.data.filepath) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 5c0691f0320..2818f251085 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -1137,7 +1137,7 @@ static void rna_ImagePreview_icon_reload(PreviewImage *prv) static PointerRNA rna_IDPreview_get(PointerRNA *ptr) { ID *id = (ID *)ptr->data; - PreviewImage *prv_img = BKE_previewimg_id_ensure(id); + PreviewImage *prv_img = BKE_previewimg_id_get(id); return rna_pointer_inherit_refine(ptr, &RNA_ImagePreview, prv_img); } @@ -1707,12 +1707,12 @@ static void rna_def_ID(BlenderRNA *brna) srna, "override_library", "IDOverrideLibrary", "Library Override", "Library override data"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - prop = RNA_def_pointer( - srna, - "preview", - "ImagePreview", - "Preview", - "Preview image and icon of this data-block (None if not supported for this type of data)"); + prop = RNA_def_pointer(srna, + "preview", + "ImagePreview", + "Preview", + "Preview image and icon of this data-block (always None if not supported " + "for this type of data)"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_pointer_funcs(prop, "rna_IDPreview_get", NULL, NULL, NULL); @@ -1826,6 +1826,13 @@ static void rna_def_ID(BlenderRNA *brna) "e.g. when calling :class:`bpy.types.Scene.update`"); RNA_def_enum_flag(func, "refresh", update_flag_items, 0, "", "Type of updates to perform"); + func = RNA_def_function(srna, "preview_ensure", "BKE_previewimg_id_ensure"); + RNA_def_function_ui_description(func, + "Ensure that this ID has preview data (if ID type supports it)"); + parm = RNA_def_pointer( + func, "preview_image", "ImagePreview", "", "The existing or created preview"); + RNA_def_function_return(func, parm); + # ifdef WITH_PYTHON RNA_def_struct_register_funcs(srna, NULL, NULL, "rna_ID_instance"); # endif -- cgit v1.2.3 From 0a7bd3b6d24a38ffa6678ef9c1d1854c6c6f27b9 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Sat, 22 May 2021 20:33:27 +0800 Subject: Fix T88464: Incorrect baking with camera markers. This will update active camera based on the maker for each frame. Reviewed By: Sebastian Parborg (zeddb), Antonio Vazquez (antoniov) Differential Revision: https://developer.blender.org/D11358 --- source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 52f7d3652a7..ef412f7fed4 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -3656,6 +3656,8 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif Scene *scene = DEG_get_evaluated_scene(depsgraph); int intersections_only = 0; /* Not used right now, but preserve for future. */ + BKE_scene_camera_switch_update(scene); + if (!scene->camera) { return false; } -- cgit v1.2.3 From 768d4c6cfefe799f5c2307fcf5fc1f90492a843f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 25 May 2021 12:14:43 +0200 Subject: Fix T88549: ID sorting tests. Forgot to initialize the ID types array... Weird though that this only failed on Windows! Thanks a lot to @deadpin for helping investigating this. --- source/blender/blenkernel/intern/lib_id_test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index 9bfd19ae4d6..fbe4a15da1c 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -37,6 +37,7 @@ struct LibIDMainSortTestContext { static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx) { + BKE_idtype_init(); ctx->bmain = BKE_main_new(); } -- cgit v1.2.3 From 405534c7560ed96efa92a3a392a5ee55e4447b3a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 25 May 2021 13:10:30 +0200 Subject: Cleanup: remove unnecessary lambda capture --- source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 8c000a536d5..a5c6d0abce0 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1025,7 +1025,7 @@ class GeometryNodesEvaluator { /* Get all origin sockets, because we have to tag those as required as well. */ Vector origin_sockets; input_socket.foreach_origin_socket( - [&, this](const DSocket origin_socket) { origin_sockets.append(origin_socket); }); + [&](const DSocket origin_socket) { origin_sockets.append(origin_socket); }); if (origin_sockets.is_empty()) { /* If there are no origin sockets, just load the value from the socket directly. */ @@ -1078,7 +1078,7 @@ class GeometryNodesEvaluator { } /* Notify origin nodes that might want to set its inputs as unused as well. */ - socket.foreach_origin_socket([&, this](const DSocket origin_socket) { + socket.foreach_origin_socket([&](const DSocket origin_socket) { if (origin_socket->is_input()) { /* Values from these sockets are loaded directly from the sockets, so there is no node to * notify. */ -- cgit v1.2.3 From 067587e32979360a280feb742d048381e57d097d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 25 May 2021 13:12:54 +0200 Subject: Cleanup: remove unused function --- release/scripts/startup/nodeitems_builtins.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index bae2c14e3d9..c900bbb074c 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -472,14 +472,6 @@ texture_node_categories = [ ]), ] - -def not_implemented_node(idname): - NodeType = getattr(bpy.types, idname) - name = NodeType.bl_rna.name - label = "%s (mockup)" % name - return NodeItem(idname, label=label) - - geometry_node_categories = [ # Geometry Nodes GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ -- cgit v1.2.3 From cccfcca33defc60d15cb8e5cd27b9ecb895be68c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 25 May 2021 13:37:50 +0200 Subject: Cleanup: use nullptr --- source/blender/nodes/shader/nodes/node_shader_curves.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index ff619306682..551c1e4e8c4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -113,7 +113,7 @@ void register_node_type_sh_curve_vec(void) node_type_init(&ntype, node_shader_init_curve_vec); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); - node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_vec); + node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec); node_type_gpu(&ntype, gpu_shader_curve_vec); nodeRegisterType(&ntype); @@ -239,7 +239,7 @@ void register_node_type_sh_curve_rgb(void) node_type_init(&ntype, node_shader_init_curve_rgb); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); - node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_rgb); + node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb); node_type_gpu(&ntype, gpu_shader_curve_rgb); nodeRegisterType(&ntype); -- cgit v1.2.3 From 0f7becece7fd829f8ccb9c0fdcb5b929fc25fb70 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 25 May 2021 15:02:31 +0200 Subject: Fix T88551: Crash accessing uninitialized tool settings Tool settings for sequencer were not initialized, which caused crash when adding strips. There was fix for same issue in versioning rB0f81dafe6cec, but subversion was not bumped, so files with uninitialized tool settings may still exist. Add `SEQ_tool_settings_get()` accessor function that will initialize tool settings if they are missing. Change operator code to use `SEQ_tool_settings_fit_method_get()` function instead of accessing tool settings directly Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D11383 --- source/blender/editors/space_sequencer/sequencer_add.c | 3 +-- source/blender/sequencer/SEQ_sequencer.h | 1 + source/blender/sequencer/intern/sequencer.c | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index a61260acfb2..68c0f4f4bdb 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -1083,8 +1083,7 @@ static int sequencer_add_image_strip_invoke(bContext *C, sequencer_disable_one_time_properties(C, op); - const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; - RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method); + RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene)); /* Name set already by drag and drop. */ if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) { diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index 85513faf3e6..236ca1a9195 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -54,6 +54,7 @@ enum { #define SEQ_DUPE_IS_RECURSIVE_CALL (1 << 4) struct SequencerToolSettings *SEQ_tool_settings_init(void); +struct SequencerToolSettings *SEQ_tool_settings_ensure(struct Scene *scene); void SEQ_tool_settings_free(struct SequencerToolSettings *tool_settings); eSeqImageFitMethod SEQ_tool_settings_fit_method_get(struct Scene *scene); void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fit_method); diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index cc11796496c..b00c36ad8e4 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -313,6 +313,17 @@ SequencerToolSettings *SEQ_tool_settings_init(void) return tool_settings; } +SequencerToolSettings *SEQ_tool_settings_ensure(Scene *scene) +{ + SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + if (tool_settings == NULL) { + scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init(); + tool_settings = scene->toolsettings->sequencer_tool_settings; + } + + return tool_settings; +} + void SEQ_tool_settings_free(SequencerToolSettings *tool_settings) { MEM_freeN(tool_settings); @@ -320,13 +331,13 @@ void SEQ_tool_settings_free(SequencerToolSettings *tool_settings) eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene) { - const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); return tool_settings->fit_method; } void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_method) { - SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); tool_settings->fit_method = fit_method; } -- cgit v1.2.3 From 1bdd5becc78bc9158752f42000b264bce719f62a Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 25 May 2021 15:09:38 +0200 Subject: Unreported fix: vertex colors overlay not set for new 3d views. Found during researching {T86956}. --- source/blender/makesdna/DNA_view3d_defaults.h | 1 + source/blender/makesrna/intern/rna_space.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesdna/DNA_view3d_defaults.h b/source/blender/makesdna/DNA_view3d_defaults.h index 10d0bafec61..9dfc37e57b1 100644 --- a/source/blender/makesdna/DNA_view3d_defaults.h +++ b/source/blender/makesdna/DNA_view3d_defaults.h @@ -70,6 +70,7 @@ \ .gpencil_paper_opacity = 0.5f, \ .gpencil_grid_opacity = 0.9f, \ + .gpencil_vertex_paint_opacity = 1.0f, \ } #define _DNA_DEFAULT_View3DCursor \ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index aa07ff4006a..d744f67c6f6 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4492,7 +4492,6 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop = RNA_def_property(srna, "gpencil_vertex_paint_opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.gpencil_vertex_paint_opacity"); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_float_default(prop, 1.0f); RNA_def_property_ui_text(prop, "Opacity", "Vertex Paint mix factor"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); } -- cgit v1.2.3 From a20ef4207d46a28c869f8953c5ecf0f5b7af1984 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 25 May 2021 15:10:46 +0200 Subject: Fix T86956: VSE shading mode ignores Grease Pencil Vertex colors. Issue is that due to the strange definition of render in grease pencil (meaning should be rendered similar to rendering). This included normal viewport rendering in OB_RENDER and OpenGL render in OB_RENDER. For other rendering modes the overlay vertex opacity would be used. This patch sets this value to 1 when rendering via a scene strip override. NOTE: that this isn't a good solution as I expect that users want to use the opacity of the Grease pencil object. Perhaps the GPencil team has a better solution for it. --- source/blender/draw/engines/gpencil/gpencil_engine.h | 4 +++- source/blender/editors/space_view3d/view3d_draw.c | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 5ceb909bc88..34fe29055d6 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -297,7 +297,9 @@ typedef struct GPENCIL_PrivateData { int v3d_color_type; /* Current frame */ int cfra; - /* If we are rendering for final render (F12). */ + /* If we are rendering for final render (F12). + * NOTE: set to false for viewport and opengl rendering (including VSE scene rendering), but set + * to true when rendering in `OB_RENDER` shading mode (viewport or opengl rendering) */ bool is_render; /* If we are in viewport display (used for VFX). */ bool is_viewport; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 0a89b7d0292..4a595c716b6 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -2039,6 +2039,9 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph, v3d.shading.type = drawtype; v3d.flag2 = V3D_HIDE_OVERLAYS; + /* HACK: When rendering gpencil objects this opacity is used to mix vertex colors in when not in + * render mode. */ + v3d.overlay.gpencil_vertex_paint_opacity = 1.0f; if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) { v3d.flag2 |= V3D_SHOW_ANNOTATION; -- cgit v1.2.3 From c3c576c8c40e34cafa75e84381248c934389a9e7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 25 May 2021 09:52:08 -0400 Subject: Cleanup: Convert to static type directly from CPPType --- .../blenkernel/intern/geometry_component_mesh.cc | 36 ++++++++-------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 2ecd0e6bd85..9e622ab2cdf 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -219,8 +219,7 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { /* We compute all interpolated values at once, because for this interpolation, one has to @@ -249,8 +248,7 @@ static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + 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 @@ -290,8 +288,7 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totpoly); @@ -329,8 +326,7 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totedge); @@ -365,8 +361,7 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totvert); @@ -394,8 +389,7 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totloop); @@ -428,8 +422,7 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totedge); @@ -467,8 +460,7 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totpoly); @@ -504,8 +496,7 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totedge); @@ -543,8 +534,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totloop); @@ -576,8 +566,7 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totvert); @@ -615,8 +604,7 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; - const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v>) { Array values(mesh.totpoly); -- cgit v1.2.3 From 54ae7baa4c3e0d4fa745f55b632148f505fad935 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Tue, 25 May 2021 22:11:06 +0800 Subject: Cleanup: Line art naming changes. Make variable naming consistent with struct names. Reviewed By: Sebastian Parborg (zeddb) Differential Revision: https://developer.blender.org/D11382 --- .../gpencil_modifiers/intern/lineart/MOD_lineart.h | 28 +- .../intern/lineart/lineart_chain.c | 586 ++++++------- .../gpencil_modifiers/intern/lineart/lineart_cpu.c | 906 ++++++++++----------- 3 files changed, 760 insertions(+), 760 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 44ff0616fe9..56b1ff87f0b 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -93,8 +93,8 @@ typedef struct LineartElementLinkNode { float crease_threshold; } LineartElementLinkNode; -typedef struct LineartLineSegment { - struct LineartLineSegment *next, *prev; +typedef struct LineartEdgeSegment { + struct LineartEdgeSegment *next, *prev; /** at==0: left at==1: right (this is in 2D projected space) */ double at; /** Occlusion level after "at" point */ @@ -107,7 +107,7 @@ typedef struct LineartLineSegment { * enough for most cases. */ unsigned char transparency_mask; -} LineartLineSegment; +} LineartEdgeSegment; typedef struct LineartVert { double gloc[3]; @@ -163,8 +163,8 @@ typedef struct LineartEdge { struct Object *object_ref; } LineartEdge; -typedef struct LineartLineChain { - struct LineartLineChain *next, *prev; +typedef struct LineartEdgeChain { + struct LineartEdgeChain *next, *prev; ListBase chain; /** Calculated before draw command. */ @@ -179,10 +179,10 @@ typedef struct LineartLineChain { unsigned char transparency_mask; struct Object *object_ref; -} LineartLineChain; +} LineartEdgeChain; -typedef struct LineartLineChainItem { - struct LineartLineChainItem *next, *prev; +typedef struct LineartEdgeChainItem { + struct LineartEdgeChainItem *next, *prev; /** Need z value for fading */ float pos[3]; /** For restoring position to 3d space */ @@ -192,12 +192,12 @@ typedef struct LineartLineChainItem { char occlusion; unsigned char transparency_mask; size_t index; -} LineartLineChainItem; +} LineartEdgeChainItem; typedef struct LineartChainRegisterEntry { struct LineartChainRegisterEntry *next, *prev; - LineartLineChain *rlc; - LineartLineChainItem *rlci; + LineartEdgeChain *ec; + LineartEdgeChainItem *eci; char picked; /* left/right mark. @@ -390,7 +390,7 @@ typedef struct LineartBoundingArea { short triangle_count; ListBase linked_triangles; - ListBase linked_lines; + ListBase linked_edges; /** Reserved for image space reduction && multi-thread chaining. */ ListBase linked_chains; @@ -529,7 +529,7 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb); void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold); void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); -int MOD_lineart_chain_count(const LineartLineChain *rlc); +int MOD_lineart_chain_count(const LineartEdgeChain *ec); void MOD_lineart_chain_clear_picked_flag(struct LineartRenderBuffer *rb); bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph, @@ -565,6 +565,6 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb, const char *vgname, int modifier_flags); -float MOD_lineart_chain_compute_length(LineartLineChain *rlc); +float MOD_lineart_chain_compute_length(LineartEdgeChain *ec); void ED_operatortypes_lineart(void); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 4ad8eed6ddd..c8e4e93843a 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -31,16 +31,16 @@ #include -#define LRT_OTHER_RV(e, rv) ((rv) == (e)->v1 ? (e)->v2 : ((rv) == (e)->v2 ? (e)->v1 : NULL)) +#define LRT_OTHER_VERT(e, vt) ((vt) == (e)->v1 ? (e)->v2 : ((vt) == (e)->v2 ? (e)->v1 : NULL)) /* Get a connected line, only for lines who has the exact given vert, or (in the case of * intersection lines) who has a vert that has the exact same position. */ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, - LineartVert *rv, - LineartVert **new_rv, + LineartVert *vt, + LineartVert **new_vt, int match_flag) { - LISTBASE_FOREACH (LinkData *, lip, &ba->linked_lines) { + LISTBASE_FOREACH (LinkData *, lip, &ba->linked_edges) { LineartEdge *n_e = lip->data; if ((!(n_e->flags & LRT_EDGE_FLAG_ALL_TYPE)) || (n_e->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) { @@ -51,18 +51,18 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, continue; } - *new_rv = LRT_OTHER_RV(n_e, rv); - if (*new_rv) { + *new_vt = LRT_OTHER_VERT(n_e, vt); + if (*new_vt) { return n_e; } if (n_e->flags & LRT_EDGE_FLAG_INTERSECTION) { - if (rv->fbcoord[0] == n_e->v1->fbcoord[0] && rv->fbcoord[1] == n_e->v1->fbcoord[1]) { - *new_rv = LRT_OTHER_RV(n_e, n_e->v1); + if (vt->fbcoord[0] == n_e->v1->fbcoord[0] && vt->fbcoord[1] == n_e->v1->fbcoord[1]) { + *new_vt = LRT_OTHER_VERT(n_e, n_e->v1); return n_e; } - if (rv->fbcoord[0] == n_e->v2->fbcoord[0] && rv->fbcoord[1] == n_e->v2->fbcoord[1]) { - *new_rv = LRT_OTHER_RV(n_e, n_e->v2); + if (vt->fbcoord[0] == n_e->v2->fbcoord[0] && vt->fbcoord[1] == n_e->v2->fbcoord[1]) { + *new_vt = LRT_OTHER_VERT(n_e, n_e->v2); return n_e; } } @@ -71,33 +71,33 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, return NULL; } -static LineartLineChain *lineart_chain_create(LineartRenderBuffer *rb) +static LineartEdgeChain *lineart_chain_create(LineartRenderBuffer *rb) { - LineartLineChain *rlc; - rlc = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineChain)); + LineartEdgeChain *ec; + ec = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChain)); - BLI_addtail(&rb->chains, rlc); + BLI_addtail(&rb->chains, ec); - return rlc; + return ec; } -static bool lineart_point_overlapping(LineartLineChainItem *rlci, +static bool lineart_point_overlapping(LineartEdgeChainItem *eci, float x, float y, double threshold) { - if (!rlci) { + if (!eci) { return false; } - if (((rlci->pos[0] + threshold) >= x) && ((rlci->pos[0] - threshold) <= x) && - ((rlci->pos[1] + threshold) >= y) && ((rlci->pos[1] - threshold) <= y)) { + if (((eci->pos[0] + threshold) >= x) && ((eci->pos[0] - threshold) <= x) && + ((eci->pos[1] + threshold) >= y) && ((eci->pos[1] - threshold) <= y)) { return true; } return false; } -static LineartLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, - LineartLineChain *rlc, +static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, + LineartEdgeChain *ec, float *fbcoord, float *gpos, float *normal, @@ -106,35 +106,35 @@ static LineartLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, unsigned char transparency_mask, size_t index) { - LineartLineChainItem *rlci; + LineartEdgeChainItem *eci; - if (lineart_point_overlapping(rlc->chain.last, fbcoord[0], fbcoord[1], 1e-5)) { + if (lineart_point_overlapping(ec->chain.last, fbcoord[0], fbcoord[1], 1e-5)) { /* Because the new chain point is overlapping, just replace the type and occlusion level of the * current point. This makes it so that the line to the point after this one has the correct * type and level. */ - LineartLineChainItem *old_rlci = rlc->chain.last; + LineartEdgeChainItem *old_rlci = ec->chain.last; old_rlci->line_type = type; old_rlci->occlusion = level; old_rlci->transparency_mask = transparency_mask; return old_rlci; } - rlci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineChainItem)); + eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(rlci->pos, fbcoord); - copy_v3_v3(rlci->gpos, gpos); - rlci->index = index; - copy_v3_v3(rlci->normal, normal); - rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; - rlci->occlusion = level; - rlci->transparency_mask = transparency_mask; - BLI_addtail(&rlc->chain, rlci); + copy_v2_v2(eci->pos, fbcoord); + copy_v3_v3(eci->gpos, gpos); + eci->index = index; + copy_v3_v3(eci->normal, normal); + eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; + eci->occlusion = level; + eci->transparency_mask = transparency_mask; + BLI_addtail(&ec->chain, eci); - return rlci; + return eci; } -static LineartLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb, - LineartLineChain *rlc, +static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb, + LineartEdgeChain *ec, float *fbcoord, float *gpos, float *normal, @@ -143,32 +143,32 @@ static LineartLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb unsigned char transparency_mask, size_t index) { - LineartLineChainItem *rlci; + LineartEdgeChainItem *eci; - if (lineart_point_overlapping(rlc->chain.first, fbcoord[0], fbcoord[1], 1e-5)) { - return rlc->chain.first; + if (lineart_point_overlapping(ec->chain.first, fbcoord[0], fbcoord[1], 1e-5)) { + return ec->chain.first; } - rlci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineChainItem)); + eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(rlci->pos, fbcoord); - copy_v3_v3(rlci->gpos, gpos); - rlci->index = index; - copy_v3_v3(rlci->normal, normal); - rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; - rlci->occlusion = level; - rlci->transparency_mask = transparency_mask; - BLI_addhead(&rlc->chain, rlci); + copy_v2_v2(eci->pos, fbcoord); + copy_v3_v3(eci->gpos, gpos); + eci->index = index; + copy_v3_v3(eci->normal, normal); + eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; + eci->occlusion = level; + eci->transparency_mask = transparency_mask; + BLI_addhead(&ec->chain, eci); - return rlci; + return eci; } void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) { - LineartLineChain *rlc; - LineartLineChainItem *rlci; + LineartEdgeChain *ec; + LineartEdgeChainItem *eci; LineartBoundingArea *ba; - LineartLineSegment *rls; + LineartEdgeSegment *es; int last_occlusion; unsigned char last_transparency; /* Used when converting from double. */ @@ -192,14 +192,14 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; - rlc = lineart_chain_create(rb); + ec = lineart_chain_create(rb); /* One chain can only have one object_ref, * so we assign it based on the first segment we found. */ - rlc->object_ref = e->object_ref; + ec->object_ref = e->object_ref; LineartEdge *new_e = e; - LineartVert *new_rv; + LineartVert *new_vt; float N[3] = {0}; if (e->t1) { @@ -218,19 +218,19 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) /* Step 1: grow left. */ ba = MOD_lineart_get_bounding_area(rb, e->v1->fbcoord[0], e->v1->fbcoord[1]); - new_rv = e->v1; - rls = e->segments.first; - VERT_COORD_TO_FLOAT(new_rv); + new_vt = e->v1; + es = e->segments.first; + VERT_COORD_TO_FLOAT(new_vt); lineart_chain_prepend_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, e->flags, - rls->occlusion, - rls->transparency_mask, + es->occlusion, + es->transparency_mask, e->v1_obindex); - while (ba && (new_e = lineart_line_get_connected(ba, new_rv, &new_rv, e->flags))) { + while (ba && (new_e = lineart_line_get_connected(ba, new_vt, &new_vt, e->flags))) { new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; if (new_e->t1 || new_e->t2) { @@ -248,41 +248,41 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) normalize_v3(N); } - if (new_rv == new_e->v1) { - for (rls = new_e->segments.last; rls; rls = rls->prev) { + if (new_vt == new_e->v1) { + for (es = new_e->segments.last; es; es = es->prev) { double gpos[3], lpos[3]; double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, new_e->flags, - rls->occlusion, - rls->transparency_mask, + es->occlusion, + es->transparency_mask, new_e->v1_obindex); - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; } } - else if (new_rv == new_e->v2) { - rls = new_e->segments.first; - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; - rls = rls->next; - for (; rls; rls = rls->next) { + else if (new_vt == new_e->v2) { + es = new_e->segments.first; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; + es = es->next; + for (; es; es = es->next) { double gpos[3], lpos[3]; double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -290,12 +290,12 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_occlusion, last_transparency, new_e->v2_obindex); - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; } VERT_COORD_TO_FLOAT(new_e->v2); lineart_chain_prepend_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -304,7 +304,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_transparency, new_e->v2_obindex); } - ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]); + ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); } /* Restore normal value. */ @@ -324,31 +324,31 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) } /* Step 2: Adding all cuts from the given line, so we can continue connecting the right side * of the line. */ - rls = e->segments.first; - last_occlusion = ((LineartLineSegment *)rls)->occlusion; - last_transparency = ((LineartLineSegment *)rls)->transparency_mask; - for (rls = rls->next; rls; rls = rls->next) { + es = e->segments.first; + last_occlusion = ((LineartEdgeSegment *)es)->occlusion; + last_transparency = ((LineartEdgeSegment *)es)->transparency_mask; + for (es = es->next; es; es = es->next) { double gpos[3], lpos[3]; double *lfb = e->v1->fbcoord, *rfb = e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, e->flags, - rls->occlusion, - rls->transparency_mask, + es->occlusion, + es->transparency_mask, e->v1_obindex); - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; } VERT_COORD_TO_FLOAT(e->v2) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -359,8 +359,8 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) /* Step 3: grow right. */ ba = MOD_lineart_get_bounding_area(rb, e->v2->fbcoord[0], e->v2->fbcoord[1]); - new_rv = e->v2; - while (ba && (new_e = lineart_line_get_connected(ba, new_rv, &new_rv, e->flags))) { + new_vt = e->v2; + while (ba && (new_e = lineart_line_get_connected(ba, new_vt, &new_vt, e->flags))) { new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; if (new_e->t1 || new_e->t2) { @@ -379,27 +379,27 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) } /* Fix leading vertex type. */ - rlci = rlc->chain.last; - rlci->line_type = new_e->flags & LRT_EDGE_FLAG_ALL_TYPE; + eci = ec->chain.last; + eci->line_type = new_e->flags & LRT_EDGE_FLAG_ALL_TYPE; - if (new_rv == new_e->v1) { - rls = new_e->segments.last; - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + if (new_vt == new_e->v1) { + es = new_e->segments.last; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; /* Fix leading vertex occlusion. */ - rlci->occlusion = last_occlusion; - rlci->transparency_mask = last_transparency; - for (rls = new_e->segments.last; rls; rls = rls->prev) { + eci->occlusion = last_occlusion; + eci->transparency_mask = last_transparency; + for (es = new_e->segments.last; es; es = es->prev) { double gpos[3], lpos[3]; double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); - last_occlusion = rls->prev ? rls->prev->occlusion : last_occlusion; - last_transparency = rls->prev ? rls->prev->transparency_mask : last_transparency; + last_occlusion = es->prev ? es->prev->occlusion : last_occlusion; + last_transparency = es->prev ? es->prev->transparency_mask : last_transparency; POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -409,35 +409,35 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->v1_obindex); } } - else if (new_rv == new_e->v2) { - rls = new_e->segments.first; - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; - rlci->occlusion = last_occlusion; - rlci->transparency_mask = last_transparency; - rls = rls->next; - for (; rls; rls = rls->next) { + else if (new_vt == new_e->v2) { + es = new_e->segments.first; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; + eci->occlusion = last_occlusion; + eci->transparency_mask = last_transparency; + es = es->next; + for (; es; es = es->next) { double gpos[3], lpos[3]; double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord; - double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]); - interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at); + double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); + interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, new_e->flags, - rls->occlusion, - rls->transparency_mask, + es->occlusion, + es->transparency_mask, new_e->v2_obindex); - last_occlusion = rls->occlusion; - last_transparency = rls->transparency_mask; + last_occlusion = es->occlusion; + last_transparency = es->transparency_mask; } VERT_COORD_TO_FLOAT(new_e->v2) lineart_chain_append_point(rb, - rlc, + ec, use_fbcoord, use_gpos, N, @@ -446,57 +446,57 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) last_transparency, new_e->v2_obindex); } - ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]); + ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); } if (rb->fuzzy_everything) { - rlc->type = LRT_EDGE_FLAG_CONTOUR; + ec->type = LRT_EDGE_FLAG_CONTOUR; } else { - rlc->type = (e->flags & LRT_EDGE_FLAG_ALL_TYPE); + ec->type = (e->flags & LRT_EDGE_FLAG_ALL_TYPE); } } LRT_ITER_ALL_LINES_END } -static LineartBoundingArea *lineart_bounding_area_get_rlci_recursive(LineartRenderBuffer *rb, - LineartBoundingArea *root, - LineartLineChainItem *rlci) +static LineartBoundingArea *lineart_bounding_area_get_eci_recursive(LineartRenderBuffer *rb, + LineartBoundingArea *root, + LineartEdgeChainItem *eci) { if (root->child == NULL) { return root; } LineartBoundingArea *ch = root->child; -#define IN_BOUND(ba, rlci) \ - ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1] +#define IN_BOUND(ba, eci) \ + ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1] - if (IN_BOUND(ch[0], rlci)) { - return lineart_bounding_area_get_rlci_recursive(rb, &ch[0], rlci); + if (IN_BOUND(ch[0], eci)) { + return lineart_bounding_area_get_eci_recursive(rb, &ch[0], eci); } - if (IN_BOUND(ch[1], rlci)) { - return lineart_bounding_area_get_rlci_recursive(rb, &ch[1], rlci); + if (IN_BOUND(ch[1], eci)) { + return lineart_bounding_area_get_eci_recursive(rb, &ch[1], eci); } - if (IN_BOUND(ch[2], rlci)) { - return lineart_bounding_area_get_rlci_recursive(rb, &ch[2], rlci); + if (IN_BOUND(ch[2], eci)) { + return lineart_bounding_area_get_eci_recursive(rb, &ch[2], eci); } - if (IN_BOUND(ch[3], rlci)) { - return lineart_bounding_area_get_rlci_recursive(rb, &ch[3], rlci); + if (IN_BOUND(ch[3], eci)) { + return lineart_bounding_area_get_eci_recursive(rb, &ch[3], eci); } #undef IN_BOUND return NULL; } static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuffer *rb, - LineartLineChainItem *rlci) + LineartEdgeChainItem *eci) { - if (!rlci) { + if (!eci) { return NULL; } - LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, rlci->pos[0], rlci->pos[1]); + LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, eci->pos[0], eci->pos[1]); if (root == NULL) { return NULL; } - return lineart_bounding_area_get_rlci_recursive(rb, root, rlci); + return lineart_bounding_area_get_eci_recursive(rb, root, eci); } /** @@ -507,61 +507,61 @@ static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuf */ static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb, LineartBoundingArea *root, - LineartLineChain *rlc, - LineartLineChainItem *rlci) + LineartEdgeChain *ec, + LineartEdgeChainItem *eci) { if (root->child == NULL) { LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized( - &root->linked_chains, &rb->render_data_pool, rlc, sizeof(LineartChainRegisterEntry)); + &root->linked_chains, &rb->render_data_pool, ec, sizeof(LineartChainRegisterEntry)); - cre->rlci = rlci; + cre->eci = eci; - if (rlci == rlc->chain.first) { + if (eci == ec->chain.first) { cre->is_left = 1; } } else { LineartBoundingArea *ch = root->child; -#define IN_BOUND(ba, rlci) \ - ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1] +#define IN_BOUND(ba, eci) \ + ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1] - if (IN_BOUND(ch[0], rlci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[0], rlc, rlci); + if (IN_BOUND(ch[0], eci)) { + lineart_bounding_area_link_point_recursive(rb, &ch[0], ec, eci); } - else if (IN_BOUND(ch[1], rlci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[1], rlc, rlci); + else if (IN_BOUND(ch[1], eci)) { + lineart_bounding_area_link_point_recursive(rb, &ch[1], ec, eci); } - else if (IN_BOUND(ch[2], rlci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[2], rlc, rlci); + else if (IN_BOUND(ch[2], eci)) { + lineart_bounding_area_link_point_recursive(rb, &ch[2], ec, eci); } - else if (IN_BOUND(ch[3], rlci)) { - lineart_bounding_area_link_point_recursive(rb, &ch[3], rlc, rlci); + else if (IN_BOUND(ch[3], eci)) { + lineart_bounding_area_link_point_recursive(rb, &ch[3], ec, eci); } #undef IN_BOUND } } -static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartLineChain *rlc) +static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartEdgeChain *ec) { - LineartLineChainItem *pl = rlc->chain.first; - LineartLineChainItem *pr = rlc->chain.last; + LineartEdgeChainItem *pl = ec->chain.first; + LineartEdgeChainItem *pr = ec->chain.last; LineartBoundingArea *ba1 = MOD_lineart_get_parent_bounding_area(rb, pl->pos[0], pl->pos[1]); LineartBoundingArea *ba2 = MOD_lineart_get_parent_bounding_area(rb, pr->pos[0], pr->pos[1]); if (ba1) { - lineart_bounding_area_link_point_recursive(rb, ba1, rlc, pl); + lineart_bounding_area_link_point_recursive(rb, ba1, ec, pl); } if (ba2) { - lineart_bounding_area_link_point_recursive(rb, ba2, rlc, pr); + lineart_bounding_area_link_point_recursive(rb, ba2, ec, pr); } } void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) { - LineartLineChain *rlc, *new_rlc; - LineartLineChainItem *rlci, *next_rlci; + LineartEdgeChain *ec, *new_rlc; + LineartEdgeChainItem *eci, *next_rlci; ListBase swap = {0}; swap.first = rb->chains.first; @@ -569,59 +569,59 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) rb->chains.last = rb->chains.first = NULL; - while ((rlc = BLI_pophead(&swap)) != NULL) { - rlc->next = rlc->prev = NULL; - BLI_addtail(&rb->chains, rlc); - LineartLineChainItem *first_rlci = (LineartLineChainItem *)rlc->chain.first; + while ((ec = BLI_pophead(&swap)) != NULL) { + ec->next = ec->prev = NULL; + BLI_addtail(&rb->chains, ec); + LineartEdgeChainItem *first_rlci = (LineartEdgeChainItem *)ec->chain.first; int fixed_occ = first_rlci->occlusion; unsigned char fixed_mask = first_rlci->transparency_mask; - rlc->level = fixed_occ; - rlc->transparency_mask = fixed_mask; - for (rlci = first_rlci->next; rlci; rlci = next_rlci) { - next_rlci = rlci->next; - if (rlci->occlusion != fixed_occ || rlci->transparency_mask != fixed_mask) { + ec->level = fixed_occ; + ec->transparency_mask = fixed_mask; + for (eci = first_rlci->next; eci; eci = next_rlci) { + next_rlci = eci->next; + if (eci->occlusion != fixed_occ || eci->transparency_mask != fixed_mask) { if (next_rlci) { - if (lineart_point_overlapping(next_rlci, rlci->pos[0], rlci->pos[1], 1e-5)) { + if (lineart_point_overlapping(next_rlci, eci->pos[0], eci->pos[1], 1e-5)) { continue; } } else { /* Set the same occlusion level for the end vertex, so when further connection is needed * the backwards occlusion info is also correct. */ - rlci->occlusion = fixed_occ; - rlci->transparency_mask = fixed_mask; + eci->occlusion = fixed_occ; + eci->transparency_mask = fixed_mask; /* No need to split at the last point anyway. */ break; } new_rlc = lineart_chain_create(rb); - new_rlc->chain.first = rlci; - new_rlc->chain.last = rlc->chain.last; - rlc->chain.last = rlci->prev; - ((LineartLineChainItem *)rlc->chain.last)->next = 0; - rlci->prev = 0; + new_rlc->chain.first = eci; + new_rlc->chain.last = ec->chain.last; + ec->chain.last = eci->prev; + ((LineartEdgeChainItem *)ec->chain.last)->next = 0; + eci->prev = 0; /* End the previous one. */ lineart_chain_append_point(rb, - rlc, - rlci->pos, - rlci->gpos, - rlci->normal, - rlci->line_type, + ec, + eci->pos, + eci->gpos, + eci->normal, + eci->line_type, fixed_occ, fixed_mask, - rlci->index); - new_rlc->object_ref = rlc->object_ref; - new_rlc->type = rlc->type; - rlc = new_rlc; - fixed_occ = rlci->occlusion; - fixed_mask = rlci->transparency_mask; - rlc->level = fixed_occ; - rlc->transparency_mask = fixed_mask; + eci->index); + new_rlc->object_ref = ec->object_ref; + new_rlc->type = ec->type; + ec = new_rlc; + fixed_occ = eci->occlusion; + fixed_mask = eci->transparency_mask; + ec->level = fixed_occ; + ec->transparency_mask = fixed_mask; } } } - LISTBASE_FOREACH (LineartLineChain *, irlc, &rb->chains) { - lineart_bounding_area_link_chain(rb, irlc); + LISTBASE_FOREACH (LineartEdgeChain *, iec, &rb->chains) { + lineart_bounding_area_link_chain(rb, iec); } } @@ -629,12 +629,12 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) * Note: segment type (crease/material/contour...) is ambiguous after this. */ static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb), - LineartLineChain *onto, - LineartLineChain *sub, + LineartEdgeChain *onto, + LineartEdgeChain *sub, int reverse_1, int reverse_2) { - LineartLineChainItem *rlci; + LineartEdgeChainItem *eci; if (onto->type == LRT_EDGE_FLAG_INTERSECTION) { if (sub->object_ref) { onto->object_ref = sub->object_ref; @@ -650,38 +650,38 @@ static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb), if (reverse_2) { /* L--R R-L. */ BLI_listbase_reverse(&sub->chain); } - rlci = sub->chain.first; - if (lineart_point_overlapping(onto->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) { + eci = sub->chain.first; + if (lineart_point_overlapping(onto->chain.last, eci->pos[0], eci->pos[1], 1e-5)) { BLI_pophead(&sub->chain); if (sub->chain.first == NULL) { return; } } - ((LineartLineChainItem *)onto->chain.last)->next = sub->chain.first; - ((LineartLineChainItem *)sub->chain.first)->prev = onto->chain.last; + ((LineartEdgeChainItem *)onto->chain.last)->next = sub->chain.first; + ((LineartEdgeChainItem *)sub->chain.first)->prev = onto->chain.last; onto->chain.last = sub->chain.last; } else { /* L-R L--R. */ if (!reverse_2) { /* R-L L--R. */ BLI_listbase_reverse(&sub->chain); } - rlci = onto->chain.first; - if (lineart_point_overlapping(sub->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) { + eci = onto->chain.first; + if (lineart_point_overlapping(sub->chain.last, eci->pos[0], eci->pos[1], 1e-5)) { BLI_pophead(&onto->chain); if (onto->chain.first == NULL) { return; } } - ((LineartLineChainItem *)sub->chain.last)->next = onto->chain.first; - ((LineartLineChainItem *)onto->chain.first)->prev = sub->chain.last; + ((LineartEdgeChainItem *)sub->chain.last)->next = onto->chain.first; + ((LineartEdgeChainItem *)onto->chain.first)->prev = sub->chain.last; onto->chain.first = sub->chain.first; } } static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuffer *rb, LineartBoundingArea *ba, - LineartLineChain *rlc, - LineartLineChainItem *rlci, + LineartEdgeChain *ec, + LineartEdgeChainItem *eci, int occlusion, unsigned char transparency_mask, float dist, @@ -694,12 +694,12 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf /* Keep using for loop because `cre` could be removed from the iteration before getting to the * next one. */ LISTBASE_FOREACH_MUTABLE (LineartChainRegisterEntry *, cre, &ba->linked_chains) { - if (cre->rlc->object_ref != rlc->object_ref) { + if (cre->ec->object_ref != ec->object_ref) { if (!rb->fuzzy_everything) { if (rb->fuzzy_intersections) { /* If none of those are intersection lines... */ - if ((!(cre->rlc->type & LRT_EDGE_FLAG_INTERSECTION)) && - (!(rlc->type & LRT_EDGE_FLAG_INTERSECTION))) { + if ((!(cre->ec->type & LRT_EDGE_FLAG_INTERSECTION)) && + (!(ec->type & LRT_EDGE_FLAG_INTERSECTION))) { continue; /* We don't want to chain along different objects at the moment. */ } } @@ -708,18 +708,18 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf } } } - if (cre->rlc->picked || cre->picked) { + if (cre->ec->picked || cre->picked) { continue; } - if (cre->rlc == rlc || (!cre->rlc->chain.first) || (cre->rlc->level != occlusion) || - (cre->rlc->transparency_mask != transparency_mask)) { + if (cre->ec == ec || (!cre->ec->chain.first) || (cre->ec->level != occlusion) || + (cre->ec->transparency_mask != transparency_mask)) { continue; } if (!rb->fuzzy_everything) { - if (cre->rlc->type != rlc->type) { + if (cre->ec->type != ec->type) { if (rb->fuzzy_intersections) { - if (!(cre->rlc->type == LRT_EDGE_FLAG_INTERSECTION || - rlc->type == LRT_EDGE_FLAG_INTERSECTION)) { + if (!(cre->ec->type == LRT_EDGE_FLAG_INTERSECTION || + ec->type == LRT_EDGE_FLAG_INTERSECTION)) { continue; /* Fuzzy intersections but no intersection line found. */ } } @@ -729,7 +729,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf } } - float new_len = len_v2v2(cre->rlci->pos, rlci->pos); + float new_len = len_v2v2(cre->eci->pos, eci->pos); if (new_len < dist) { closest_cre = cre; dist = new_len; @@ -748,7 +748,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf LISTBASE_FOREACH (LinkData *, ld, list) { \ LineartBoundingArea *sba = (LineartBoundingArea *)ld->data; \ adjacent_closest = lineart_chain_get_closest_cre( \ - rb, sba, rlc, rlci, occlusion, transparency_mask, dist, &adjacent_new_len, ba); \ + rb, sba, ec, eci, occlusion, transparency_mask, dist, &adjacent_new_len, ba); \ if (adjacent_new_len < dist) { \ dist = adjacent_new_len; \ closest_cre = adjacent_closest; \ @@ -756,10 +756,10 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf } \ } if (!caller_ba) { - LRT_TEST_ADJACENT_AREAS(rlci->pos[0] - ba->l, &ba->lp); - LRT_TEST_ADJACENT_AREAS(ba->r - rlci->pos[0], &ba->rp); - LRT_TEST_ADJACENT_AREAS(ba->u - rlci->pos[1], &ba->up); - LRT_TEST_ADJACENT_AREAS(rlci->pos[1] - ba->b, &ba->bp); + LRT_TEST_ADJACENT_AREAS(eci->pos[0] - ba->l, &ba->lp); + LRT_TEST_ADJACENT_AREAS(ba->r - eci->pos[0], &ba->rp); + LRT_TEST_ADJACENT_AREAS(ba->u - eci->pos[1], &ba->up); + LRT_TEST_ADJACENT_AREAS(eci->pos[1] - ba->b, &ba->bp); } if (result_new_len) { (*result_new_len) = dist; @@ -774,8 +774,8 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf */ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) { - LineartLineChain *rlc; - LineartLineChainItem *rlci_l, *rlci_r; + LineartEdgeChain *ec; + LineartEdgeChainItem *rlci_l, *rlci_r; LineartBoundingArea *ba_l, *ba_r; LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre; float dist = rb->chaining_image_threshold; @@ -793,24 +793,24 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) rb->chains.last = rb->chains.first = NULL; - while ((rlc = BLI_pophead(&swap)) != NULL) { - rlc->next = rlc->prev = NULL; - if (rlc->picked) { + while ((ec = BLI_pophead(&swap)) != NULL) { + ec->next = ec->prev = NULL; + if (ec->picked) { continue; } - BLI_addtail(&rb->chains, rlc); + BLI_addtail(&rb->chains, ec); - occlusion = rlc->level; - transparency_mask = rlc->transparency_mask; + occlusion = ec->level; + transparency_mask = ec->transparency_mask; - rlci_l = rlc->chain.first; - rlci_r = rlc->chain.last; + rlci_l = ec->chain.first; + rlci_r = ec->chain.last; while ((ba_l = lineart_bounding_area_get_end_point(rb, rlci_l)) && (ba_r = lineart_bounding_area_get_end_point(rb, rlci_r))) { closest_cre_l = lineart_chain_get_closest_cre( - rb, ba_l, rlc, rlci_l, occlusion, transparency_mask, dist, &dist_l, NULL); + rb, ba_l, ec, rlci_l, occlusion, transparency_mask, dist, &dist_l, NULL); closest_cre_r = lineart_chain_get_closest_cre( - rb, ba_r, rlc, rlci_r, occlusion, transparency_mask, dist, &dist_r, NULL); + rb, ba_r, ec, rlci_r, occlusion, transparency_mask, dist, &dist_r, NULL); if (closest_cre_l && closest_cre_r) { if (dist_l < dist_r) { closest_cre = closest_cre_l; @@ -834,56 +834,56 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) break; } closest_cre->picked = 1; - closest_cre->rlc->picked = 1; + closest_cre->ec->picked = 1; if (closest_cre->is_left) { - lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 0); + lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 0); } else { - lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 1); + lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 1); } - BLI_remlink(&swap, closest_cre->rlc); - rlci_l = rlc->chain.first; - rlci_r = rlc->chain.last; + BLI_remlink(&swap, closest_cre->ec); + rlci_l = ec->chain.first; + rlci_r = ec->chain.last; } - rlc->picked = 1; + ec->picked = 1; } } /** * Length is in image space. */ -float MOD_lineart_chain_compute_length(LineartLineChain *rlc) +float MOD_lineart_chain_compute_length(LineartEdgeChain *ec) { - LineartLineChainItem *rlci; + LineartEdgeChainItem *eci; float offset_accum = 0; float dist; float last_point[2]; - rlci = rlc->chain.first; - copy_v2_v2(last_point, rlci->pos); - for (rlci = rlc->chain.first; rlci; rlci = rlci->next) { - dist = len_v2v2(rlci->pos, last_point); + eci = ec->chain.first; + copy_v2_v2(last_point, eci->pos); + for (eci = ec->chain.first; eci; eci = eci->next) { + dist = len_v2v2(eci->pos, last_point); offset_accum += dist; - copy_v2_v2(last_point, rlci->pos); + copy_v2_v2(last_point, eci->pos); } return offset_accum; } void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold) { - LineartLineChain *rlc, *next_rlc; - for (rlc = rb->chains.first; rlc; rlc = next_rlc) { - next_rlc = rlc->next; - if (MOD_lineart_chain_compute_length(rlc) < threshold) { - BLI_remlink(&rb->chains, rlc); + LineartEdgeChain *ec, *next_rlc; + for (ec = rb->chains.first; ec; ec = next_rlc) { + next_rlc = ec->next; + if (MOD_lineart_chain_compute_length(ec) < threshold) { + BLI_remlink(&rb->chains, ec); } } } -int MOD_lineart_chain_count(const LineartLineChain *rlc) +int MOD_lineart_chain_count(const LineartEdgeChain *ec) { int count = 0; - LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) { + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { count++; } return count; @@ -894,8 +894,8 @@ void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb) if (rb == NULL) { return; } - LISTBASE_FOREACH (LineartLineChain *, rlc, &rb->chains) { - rlc->picked = 0; + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + ec->picked = 0; } } @@ -905,8 +905,8 @@ void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb) */ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad) { - LineartLineChain *rlc, *new_rlc; - LineartLineChainItem *rlci, *next_rlci, *prev_rlci; + LineartEdgeChain *ec, *new_rlc; + LineartEdgeChainItem *eci, *next_rlci, *prev_rlci; ListBase swap = {0}; swap.first = rb->chains.first; @@ -914,43 +914,43 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol rb->chains.last = rb->chains.first = NULL; - while ((rlc = BLI_pophead(&swap)) != NULL) { - rlc->next = rlc->prev = NULL; - BLI_addtail(&rb->chains, rlc); - LineartLineChainItem *first_rlci = (LineartLineChainItem *)rlc->chain.first; - for (rlci = first_rlci->next; rlci; rlci = next_rlci) { - next_rlci = rlci->next; - prev_rlci = rlci->prev; + while ((ec = BLI_pophead(&swap)) != NULL) { + ec->next = ec->prev = NULL; + BLI_addtail(&rb->chains, ec); + LineartEdgeChainItem *first_rlci = (LineartEdgeChainItem *)ec->chain.first; + for (eci = first_rlci->next; eci; eci = next_rlci) { + next_rlci = eci->next; + prev_rlci = eci->prev; float angle = M_PI; if (next_rlci && prev_rlci) { - angle = angle_v2v2v2(prev_rlci->pos, rlci->pos, next_rlci->pos); + angle = angle_v2v2v2(prev_rlci->pos, eci->pos, next_rlci->pos); } else { break; /* No need to split at the last point anyway.*/ } if (angle < angle_threshold_rad) { new_rlc = lineart_chain_create(rb); - new_rlc->chain.first = rlci; - new_rlc->chain.last = rlc->chain.last; - rlc->chain.last = rlci->prev; - ((LineartLineChainItem *)rlc->chain.last)->next = 0; - rlci->prev = 0; + new_rlc->chain.first = eci; + new_rlc->chain.last = ec->chain.last; + ec->chain.last = eci->prev; + ((LineartEdgeChainItem *)ec->chain.last)->next = 0; + eci->prev = 0; /* End the previous one. */ lineart_chain_append_point(rb, - rlc, - rlci->pos, - rlci->gpos, - rlci->normal, - rlci->line_type, - rlc->level, - rlci->transparency_mask, - rlci->index); - new_rlc->object_ref = rlc->object_ref; - new_rlc->type = rlc->type; - new_rlc->level = rlc->level; - new_rlc->transparency_mask = rlc->transparency_mask; - rlc = new_rlc; + ec, + eci->pos, + eci->gpos, + eci->normal, + eci->line_type, + ec->level, + eci->transparency_mask, + eci->index); + new_rlc->object_ref = ec->object_ref; + new_rlc->type = ec->type; + new_rlc->level = ec->level; + new_rlc->transparency_mask = ec->transparency_mask; + ec = new_rlc; } } } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index ef412f7fed4..d43b9a3426f 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -63,7 +63,7 @@ static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb, LineartEdge *e); -static void lineart_bounding_area_link_line(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, LineartBoundingArea *root_ba, LineartEdge *e); @@ -86,14 +86,14 @@ static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb, static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, LineartBoundingArea *root_ba, - LineartTriangle *rt, + LineartTriangle *tri, double *LRUB, int recursive, int recursive_level, bool do_intersection); static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, - const LineartTriangle *rt, + const LineartTriangle *tri, const LineartEdge *e, const double *override_camera_loc, const bool override_cam_is_persp, @@ -107,35 +107,35 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e); -static void lineart_discard_segment(LineartRenderBuffer *rb, LineartLineSegment *rls) +static void lineart_discard_segment(LineartRenderBuffer *rb, LineartEdgeSegment *es) { BLI_spin_lock(&rb->lock_cuts); - memset(rls, 0, sizeof(LineartLineSegment)); + memset(es, 0, sizeof(LineartEdgeSegment)); /* Storing the node for potentially reuse the memory for new segment data. * Line Art data is not freed after all calculations are done. */ - BLI_addtail(&rb->wasted_cuts, rls); + BLI_addtail(&rb->wasted_cuts, es); BLI_spin_unlock(&rb->lock_cuts); } -static LineartLineSegment *lineart_give_segment(LineartRenderBuffer *rb) +static LineartEdgeSegment *lineart_give_segment(LineartRenderBuffer *rb) { BLI_spin_lock(&rb->lock_cuts); /* See if there is any already allocated memory we can reuse. */ if (rb->wasted_cuts.first) { - LineartLineSegment *rls = (LineartLineSegment *)BLI_pophead(&rb->wasted_cuts); + LineartEdgeSegment *es = (LineartEdgeSegment *)BLI_pophead(&rb->wasted_cuts); BLI_spin_unlock(&rb->lock_cuts); - memset(rls, 0, sizeof(LineartLineSegment)); - return rls; + memset(es, 0, sizeof(LineartEdgeSegment)); + return es; } BLI_spin_unlock(&rb->lock_cuts); /* Otherwise allocate some new memory. */ - return (LineartLineSegment *)lineart_mem_acquire_thread(&rb->render_data_pool, - sizeof(LineartLineSegment)); + return (LineartEdgeSegment *)lineart_mem_acquire_thread(&rb->render_data_pool, + sizeof(LineartEdgeSegment)); } /** @@ -144,9 +144,9 @@ static LineartLineSegment *lineart_give_segment(LineartRenderBuffer *rb) static void lineart_edge_cut( LineartRenderBuffer *rb, LineartEdge *e, double start, double end, uchar transparency_mask) { - LineartLineSegment *rls, *irls, *next_rls, *prev_rls; - LineartLineSegment *cut_start_before = 0, *cut_end_before = 0; - LineartLineSegment *ns = 0, *ns2 = 0; + LineartEdgeSegment *es, *ies, *next_es, *prev_es; + LineartEdgeSegment *cut_start_before = 0, *cut_end_before = 0; + LineartEdgeSegment *ns = 0, *ns2 = 0; int untouched = 0; /* If for some reason the occlusion function may give a result that has zero length, or reversed @@ -173,18 +173,18 @@ static void lineart_edge_cut( /* Begin looking for starting position of the segment. */ /* Not using a list iteration macro because of it more clear when using for loops to iterate * through the segments. */ - for (rls = e->segments.first; rls; rls = rls->next) { - if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, start)) { - cut_start_before = rls; + for (es = e->segments.first; es; es = es->next) { + if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, start)) { + cut_start_before = es; ns = cut_start_before; break; } - if (rls->next == NULL) { + if (es->next == NULL) { break; } - irls = rls->next; - if (irls->at > start + 1e-09 && start > rls->at) { - cut_start_before = irls; + ies = es->next; + if (ies->at > start + 1e-09 && start > es->at) { + cut_start_before = ies; ns = lineart_give_segment(rb); break; } @@ -192,25 +192,25 @@ static void lineart_edge_cut( if (!cut_start_before && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { untouched = 1; } - for (rls = cut_start_before; rls; rls = rls->next) { + for (es = cut_start_before; es; es = es->next) { /* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle * strip). */ - if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, end)) { - cut_end_before = rls; + if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, end)) { + cut_end_before = es; ns2 = cut_end_before; break; } - /* This check is to prevent `rls->at == 1.0` (where we don't need to cut because we are at the + /* This check is to prevent `es->at == 1.0` (where we don't need to cut because we are at the * end point). */ - if (!rls->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { - cut_end_before = rls; + if (!es->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { + cut_end_before = es; ns2 = cut_end_before; untouched = 1; break; } /* When an actual cut is needed in the line. */ - if (rls->at > end) { - cut_end_before = rls; + if (es->at > end) { + cut_end_before = es; ns2 = lineart_give_segment(rb); break; } @@ -233,9 +233,9 @@ static void lineart_edge_cut( if (cut_start_before) { if (cut_start_before != ns) { /* Insert cutting points for when a new cut is needed. */ - irls = cut_start_before->prev ? cut_start_before->prev : NULL; - ns->occlusion = irls ? irls->occlusion : 0; - ns->transparency_mask = irls->transparency_mask; + ies = cut_start_before->prev ? cut_start_before->prev : NULL; + ns->occlusion = ies ? ies->occlusion : 0; + ns->transparency_mask = ies->transparency_mask; BLI_insertlinkbefore(&e->segments, cut_start_before, ns); } /* Otherwise we already found a existing cutting point, no need to insert a new one. */ @@ -243,24 +243,24 @@ static void lineart_edge_cut( else { /* We have yet to reach a existing cutting point even after we searched the whole line, so we * append the new cut to the end. */ - irls = e->segments.last; - ns->occlusion = irls->occlusion; - ns->transparency_mask = irls->transparency_mask; + ies = e->segments.last; + ns->occlusion = ies->occlusion; + ns->transparency_mask = ies->transparency_mask; BLI_addtail(&e->segments, ns); } if (cut_end_before) { /* The same manipulation as on "cut_start_before". */ if (cut_end_before != ns2) { - irls = cut_end_before->prev ? cut_end_before->prev : NULL; - ns2->occlusion = irls ? irls->occlusion : 0; - ns2->transparency_mask = irls ? irls->transparency_mask : 0; + ies = cut_end_before->prev ? cut_end_before->prev : NULL; + ns2->occlusion = ies ? ies->occlusion : 0; + ns2->transparency_mask = ies ? ies->transparency_mask : 0; BLI_insertlinkbefore(&e->segments, cut_end_before, ns2); } } else { - irls = e->segments.last; - ns2->occlusion = irls->occlusion; - ns2->transparency_mask = irls->transparency_mask; + ies = e->segments.last; + ns2->occlusion = ies->occlusion; + ns2->transparency_mask = ies->transparency_mask; BLI_addtail(&e->segments, ns2); } @@ -276,29 +276,29 @@ static void lineart_edge_cut( } /* Register 1 level of occlusion for all touched segments. */ - for (rls = ns; rls && rls != ns2; rls = rls->next) { - rls->occlusion++; - rls->transparency_mask |= transparency_mask; + for (es = ns; es && es != ns2; es = es->next) { + es->occlusion++; + es->transparency_mask |= transparency_mask; } /* Reduce adjacent cutting points of the same level, which saves memory. */ char min_occ = 127; - prev_rls = NULL; - for (rls = e->segments.first; rls; rls = next_rls) { - next_rls = rls->next; + prev_es = NULL; + for (es = e->segments.first; es; es = next_es) { + next_es = es->next; - if (prev_rls && prev_rls->occlusion == rls->occlusion && - prev_rls->transparency_mask == rls->transparency_mask) { - BLI_remlink(&e->segments, rls); + if (prev_es && prev_es->occlusion == es->occlusion && + prev_es->transparency_mask == es->transparency_mask) { + BLI_remlink(&e->segments, es); /* This puts the node back to the render buffer, if more cut happens, these unused nodes get * picked first. */ - lineart_discard_segment(rb, rls); + lineart_discard_segment(rb, es); continue; } - min_occ = MIN2(min_occ, rls->occlusion); + min_occ = MIN2(min_occ, es->occlusion); - prev_rls = rls; + prev_es = es; } e->min_occ = min_occ; } @@ -306,12 +306,12 @@ static void lineart_edge_cut( /** * To see if given line is connected to an adjacent intersection line. */ -BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *rt) +BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *tri) { LineartVertIntersection *v1 = (void *)e->v1; LineartVertIntersection *v2 = (void *)e->v2; - return ((v1->base.flag && v1->intersecting_with == rt) || - (v2->base.flag && v2->intersecting_with == rt)); + return ((v1->base.flag && v1->intersecting_with == tri) || + (v2->base.flag && v2->intersecting_with == tri)); } static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *e, int thread_id) @@ -319,7 +319,7 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * double x = e->v1->fbcoord[0], y = e->v1->fbcoord[1]; LineartBoundingArea *ba = lineart_edge_first_bounding_area(rb, e); LineartBoundingArea *nba = ba; - LineartTriangleThread *rt; + LineartTriangleThread *tri; /* These values are used for marching along the line. */ double l, r; @@ -335,15 +335,15 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * while (nba) { LISTBASE_FOREACH (LinkData *, lip, &nba->linked_triangles) { - rt = lip->data; + tri = lip->data; /* If we are already testing the line in this thread, then don't do it. */ - if (rt->testing_e[thread_id] == e || (rt->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) || - lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)rt)) { + if (tri->testing_e[thread_id] == e || (tri->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) || + lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)tri)) { continue; } - rt->testing_e[thread_id] = e; + tri->testing_e[thread_id] = e; if (lineart_triangle_edge_image_space_occlusion(&rb->lock_task, - (const LineartTriangle *)rt, + (const LineartTriangle *)tri, e, rb->camera_pos, rb->cam_is_persp, @@ -354,7 +354,7 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * rb->shift_y, &l, &r)) { - lineart_edge_cut(rb, e, l, r, rt->base.transparency_mask); + lineart_edge_cut(rb, e, l, r, tri->base.transparency_mask); if (e->min_occ > rb->max_occlusion_level) { /* No need to calculate any longer on this line because no level more than set value is * going to show up in the rendered result. */ @@ -678,24 +678,24 @@ static LineartElementLinkNode *lineart_memory_get_edge_space(LineartRenderBuffer return reln; } -static void lineart_triangle_post(LineartTriangle *rt, LineartTriangle *orig) +static void lineart_triangle_post(LineartTriangle *tri, LineartTriangle *orig) { /* Just re-assign normal and set cull flag. */ - copy_v3_v3_db(rt->gn, orig->gn); - rt->flags = LRT_CULL_GENERATED; + copy_v3_v3_db(tri->gn, orig->gn); + tri->flags = LRT_CULL_GENERATED; } -static void lineart_triangle_set_cull_flag(LineartTriangle *rt, uchar flag) +static void lineart_triangle_set_cull_flag(LineartTriangle *tri, uchar flag) { - uchar intersection_only = (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY); - rt->flags = flag; - rt->flags |= intersection_only; + uchar intersection_only = (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY); + tri->flags = flag; + tri->flags |= intersection_only; } -static bool lineart_edge_match(LineartTriangle *rt, LineartEdge *e, int v1, int v2) +static bool lineart_edge_match(LineartTriangle *tri, LineartEdge *e, int v1, int v2) { - return ((rt->v[v1] == e->v1 && rt->v[v2] == e->v2) || - (rt->v[v2] == e->v1 && rt->v[v1] == e->v2)); + return ((tri->v[v1] == e->v1 && tri->v[v2] == e->v2) || + (tri->v[v2] == e->v1 && tri->v[v1] == e->v2)); } /** @@ -703,7 +703,7 @@ static bool lineart_edge_match(LineartTriangle *rt, LineartEdge *e, int v1, int * reversed by the caller so don't need to implement one in a different direction. */ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, - LineartTriangle *rt, + LineartTriangle *tri, int in0, int in1, int in2, @@ -728,26 +728,26 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, char new_flag = 0; LineartEdge *new_e, *e, *old_e; - LineartLineSegment *rls; - LineartTriangleAdjacent *rta; + LineartEdgeSegment *es; + LineartTriangleAdjacent *ta; - if (rt->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) { + if (tri->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) { return; } - /* See definition of rt->intersecting_verts and the usage in + /* See definition of tri->intersecting_verts and the usage in * lineart_geometry_object_load() for details. */ - rta = (void *)rt->intersecting_verts; + ta = (void *)tri->intersecting_verts; - LineartVert *rv = &((LineartVert *)v_eln->pointer)[v_count]; - LineartTriangle *rt1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count); - LineartTriangle *rt2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1)); + LineartVert *vt = &((LineartVert *)v_eln->pointer)[v_count]; + LineartTriangle *tri1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count); + LineartTriangle *tri2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1)); new_e = &((LineartEdge *)e_eln->pointer)[e_count]; - /* Init `rl` to the last `rl` entry. */ + /* Init `edge` to the last `edge` entry. */ e = new_e; -#define INCREASE_RL \ +#define INCREASE_EDGE \ e_count++; \ v1_obi = e->v1_obindex; \ v2_obi = e->v2_obindex; \ @@ -755,40 +755,40 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, e = new_e; \ e->v1_obindex = v1_obi; \ e->v2_obindex = v2_obi; \ - rls = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineSegment)); \ - BLI_addtail(&e->segments, rls); + es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); \ + BLI_addtail(&e->segments, es); -#define SELECT_RL(e_num, v1_link, v2_link, newrt) \ - if (rta->e[e_num]) { \ - old_e = rta->e[e_num]; \ +#define SELECT_EDGE(e_num, v1_link, v2_link, new_tri) \ + if (ta->e[e_num]) { \ + old_e = ta->e[e_num]; \ new_flag = old_e->flags; \ old_e->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - INCREASE_RL \ + INCREASE_EDGE \ e->v1 = (v1_link); \ e->v2 = (v2_link); \ e->flags = new_flag; \ e->object_ref = ob; \ - e->t1 = ((old_e->t1 == rt) ? (newrt) : (old_e->t1)); \ - e->t2 = ((old_e->t2 == rt) ? (newrt) : (old_e->t2)); \ + e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ + e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ lineart_add_edge_to_list(rb, e); \ } -#define RELINK_RL(e_num, newrt) \ - if (rta->e[e_num]) { \ - old_e = rta->e[e_num]; \ - old_e->t1 = ((old_e->t1 == rt) ? (newrt) : (old_e->t1)); \ - old_e->t2 = ((old_e->t2 == rt) ? (newrt) : (old_e->t2)); \ +#define RELINK_EDGE(e_num, new_tri) \ + if (ta->e[e_num]) { \ + old_e = ta->e[e_num]; \ + old_e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ + old_e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ } -#define REMOVE_TRIANGLE_RL \ - if (rta->e[0]) { \ - rta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ +#define REMOVE_TRIANGLE_EDGE \ + if (ta->e[0]) { \ + ta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ } \ - if (rta->e[1]) { \ - rta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + if (ta->e[1]) { \ + ta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ } \ - if (rta->e[2]) { \ - rta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + if (ta->e[2]) { \ + ta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ } switch (in0 + in1 + in2) { @@ -797,13 +797,13 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, case 3: /* Triangle completely behind near plane, throw it away * also remove render lines form being computed. */ - lineart_triangle_set_cull_flag(rt, LRT_CULL_DISCARD); - REMOVE_TRIANGLE_RL + lineart_triangle_set_cull_flag(tri, LRT_CULL_DISCARD); + REMOVE_TRIANGLE_EDGE return; case 2: /* Two points behind near plane, cut those and * generate 2 new points, 3 lines and 1 triangle. */ - lineart_triangle_set_cull_flag(rt, LRT_CULL_USED); + lineart_triangle_set_cull_flag(tri, LRT_CULL_USED); /** * (!in0) means "when point 0 is visible". @@ -828,136 +828,136 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, if (!in0) { /* Cut point for line 2---|-----0. */ - sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc); + sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); /* Assign it to a new point. */ - interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[2]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[2]->index; /* Cut point for line 1---|-----0. */ - sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc); + sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); /* Assign it to another new point. */ - interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[1]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[1]->index; /* New line connecting two new points. */ - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; lineart_prepend_edge_direct(&rb->contours, e); } /* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as - * `rt->rl` and `rt->v` has the same sequence. and the winding direction + * `tri->edge` and `tri->v` has the same sequence. and the winding direction * can be either CW or CCW but needs to be consistent throughout the calculation. */ - e->v1 = &rv[1]; - e->v2 = &rv[0]; + e->v1 = &vt[1]; + e->v2 = &vt[0]; /* Only one adjacent triangle, because the other side is the near plane. */ /* Use `tl` or `tr` doesn't matter. */ - e->t1 = rt1; + e->t1 = tri1; e->object_ref = ob; /* New line connecting original point 0 and a new point, only when it's a selected line. */ - SELECT_RL(2, rt->v[0], &rv[0], rt1) + SELECT_EDGE(2, tri->v[0], &vt[0], tri1) /* New line connecting original point 0 and another new point. */ - SELECT_RL(0, rt->v[0], &rv[1], rt1) + SELECT_EDGE(0, tri->v[0], &vt[1], tri1) /* Re-assign triangle point array to two new points. */ - rt1->v[0] = rt->v[0]; - rt1->v[1] = &rv[1]; - rt1->v[2] = &rv[0]; + tri1->v[0] = tri->v[0]; + tri1->v[1] = &vt[1]; + tri1->v[2] = &vt[0]; - lineart_triangle_post(rt1, rt); + lineart_triangle_post(tri1, tri); v_count += 2; t_count += 1; } else if (!in2) { - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[0]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[0]->index; - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[1]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[1]->index; - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; lineart_prepend_edge_direct(&rb->contours, e); } - e->v1 = &rv[0]; - e->v2 = &rv[1]; - e->t1 = rt1; + e->v1 = &vt[0]; + e->v2 = &vt[1]; + e->t1 = tri1; e->object_ref = ob; - SELECT_RL(2, rt->v[2], &rv[0], rt1) - SELECT_RL(1, rt->v[2], &rv[1], rt1) + SELECT_EDGE(2, tri->v[2], &vt[0], tri1) + SELECT_EDGE(1, tri->v[2], &vt[1], tri1) - rt1->v[0] = &rv[0]; - rt1->v[1] = &rv[1]; - rt1->v[2] = rt->v[2]; + tri1->v[0] = &vt[0]; + tri1->v[1] = &vt[1]; + tri1->v[2] = tri->v[2]; - lineart_triangle_post(rt1, rt); + lineart_triangle_post(tri1, tri); v_count += 2; t_count += 1; } else if (!in1) { - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[2]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[2]->index; - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[0]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[0]->index; - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; lineart_prepend_edge_direct(&rb->contours, e); } - e->v1 = &rv[1]; - e->v2 = &rv[0]; - e->t1 = rt1; + e->v1 = &vt[1]; + e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; - SELECT_RL(1, rt->v[1], &rv[0], rt1) - SELECT_RL(0, rt->v[1], &rv[1], rt1) + SELECT_EDGE(1, tri->v[1], &vt[0], tri1) + SELECT_EDGE(0, tri->v[1], &vt[1], tri1) - rt1->v[0] = &rv[0]; - rt1->v[1] = rt->v[1]; - rt1->v[2] = &rv[1]; + tri1->v[0] = &vt[0]; + tri1->v[1] = tri->v[1]; + tri1->v[2] = &vt[1]; - lineart_triangle_post(rt1, rt); + lineart_triangle_post(tri1, tri); v_count += 2; t_count += 1; @@ -966,7 +966,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, case 1: /* One point behind near plane, cut those and * generate 2 new points, 4 lines and 2 triangles. */ - lineart_triangle_set_cull_flag(rt, LRT_CULL_USED); + lineart_triangle_set_cull_flag(tri, LRT_CULL_USED); /** * (in0) means "when point 0 is invisible". @@ -993,152 +993,152 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, */ if (in0) { /* Cut point for line 0---|------1. */ - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot2 / (dot1 + dot2); /* Assign to a new point. */ - interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[0]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[0]->index; /* Cut point for line 0---|------2. */ - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot2 / (dot1 + dot2); /* Assign to other new point. */ - interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[0]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[0]->index; /* New line connects two new points. */ - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; lineart_prepend_edge_direct(&rb->contours, e); } - e->v1 = &rv[1]; - e->v2 = &rv[0]; - e->t1 = rt1; + e->v1 = &vt[1]; + e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; /* New line connects new point 0 and old point 1, * this is a border line. */ - SELECT_RL(0, rt->v[1], &rv[0], rt1) - SELECT_RL(2, rt->v[2], &rv[1], rt2) - RELINK_RL(1, rt2) + SELECT_EDGE(0, tri->v[1], &vt[0], tri1) + SELECT_EDGE(2, tri->v[2], &vt[1], tri2) + RELINK_EDGE(1, tri2) /* We now have one triangle closed. */ - rt1->v[0] = rt->v[1]; - rt1->v[1] = &rv[1]; - rt1->v[2] = &rv[0]; + tri1->v[0] = tri->v[1]; + tri1->v[1] = &vt[1]; + tri1->v[2] = &vt[0]; /* Close the second triangle. */ - rt2->v[0] = &rv[1]; - rt2->v[1] = rt->v[1]; - rt2->v[2] = rt->v[2]; + tri2->v[0] = &vt[1]; + tri2->v[1] = tri->v[1]; + tri2->v[2] = tri->v[2]; - lineart_triangle_post(rt1, rt); - lineart_triangle_post(rt2, rt); + lineart_triangle_post(tri1, tri); + lineart_triangle_post(tri2, tri); v_count += 2; t_count += 2; } else if (in1) { - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[1]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[1]->index; - sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[1]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[1]->index; - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; lineart_prepend_edge_direct(&rb->contours, e); } - e->v1 = &rv[1]; - e->v2 = &rv[0]; - e->t1 = rt1; + e->v1 = &vt[1]; + e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; - SELECT_RL(1, rt->v[2], &rv[0], rt1) - SELECT_RL(0, rt->v[0], &rv[1], rt2) - RELINK_RL(2, rt2) + SELECT_EDGE(1, tri->v[2], &vt[0], tri1) + SELECT_EDGE(0, tri->v[0], &vt[1], tri2) + RELINK_EDGE(2, tri2) - rt1->v[0] = rt->v[2]; - rt1->v[1] = &rv[1]; - rt1->v[2] = &rv[0]; + tri1->v[0] = tri->v[2]; + tri1->v[1] = &vt[1]; + tri1->v[2] = &vt[0]; - rt2->v[0] = &rv[1]; - rt2->v[1] = rt->v[2]; - rt2->v[2] = rt->v[0]; + tri2->v[0] = &vt[1]; + tri2->v[1] = tri->v[2]; + tri2->v[2] = tri->v[0]; - lineart_triangle_post(rt1, rt); - lineart_triangle_post(rt2, rt); + lineart_triangle_post(tri1, tri); + lineart_triangle_post(tri2, tri); v_count += 2; t_count += 2; } else if (in2) { - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a); - mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc); - rv[0].index = rt->v[2]->index; + interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a); + mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + vt[0].index = tri->v[2]->index; - sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc); + sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); dot1 = dot_v3v3_db(vv1, view_dir); dot2 = dot_v3v3_db(vv2, view_dir); a = dot1 / (dot1 + dot2); - interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a); - mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc); - rv[1].index = rt->v[2]->index; + interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a); + mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + vt[1].index = tri->v[2]->index; - INCREASE_RL + INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; lineart_prepend_edge_direct(&rb->contours, e); } - e->v1 = &rv[1]; - e->v2 = &rv[0]; - e->t1 = rt1; + e->v1 = &vt[1]; + e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; - SELECT_RL(2, rt->v[0], &rv[0], rt1) - SELECT_RL(1, rt->v[1], &rv[1], rt2) - RELINK_RL(0, rt2) + SELECT_EDGE(2, tri->v[0], &vt[0], tri1) + SELECT_EDGE(1, tri->v[1], &vt[1], tri2) + RELINK_EDGE(0, tri2) - rt1->v[0] = rt->v[0]; - rt1->v[1] = &rv[1]; - rt1->v[2] = &rv[0]; + tri1->v[0] = tri->v[0]; + tri1->v[1] = &vt[1]; + tri1->v[2] = &vt[0]; - rt2->v[0] = &rv[1]; - rt2->v[1] = rt->v[0]; - rt2->v[2] = rt->v[1]; + tri2->v[0] = &vt[1]; + tri2->v[1] = tri->v[0]; + tri2->v[2] = tri->v[1]; - lineart_triangle_post(rt1, rt); - lineart_triangle_post(rt2, rt); + lineart_triangle_post(tri1, tri); + lineart_triangle_post(tri2, tri); v_count += 2; t_count += 2; @@ -1149,10 +1149,10 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, *r_e_count = e_count; *r_t_count = t_count; -#undef INCREASE_RL -#undef SELECT_RL -#undef RELINK_RL -#undef REMOVE_TRIANGLE_RL +#undef INCREASE_EDGE +#undef SELECT_EDGE +#undef RELINK_EDGE +#undef REMOVE_TRIANGLE_EDGE } /** @@ -1163,7 +1163,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, */ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) { - LineartTriangle *rt; + LineartTriangle *tri; LineartElementLinkNode *v_eln, *t_eln, *e_eln; double(*vp)[4] = rb->view_projection; int i; @@ -1219,25 +1219,25 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) in0 = 0, in1 = 0, in2 = 0; \ if (clip_far) { \ /* Point outside far plane. */ \ - if (rt->v[0]->fbcoord[use_w] > clip_end) { \ + if (tri->v[0]->fbcoord[use_w] > clip_end) { \ in0 = 1; \ } \ - if (rt->v[1]->fbcoord[use_w] > clip_end) { \ + if (tri->v[1]->fbcoord[use_w] > clip_end) { \ in1 = 1; \ } \ - if (rt->v[2]->fbcoord[use_w] > clip_end) { \ + if (tri->v[2]->fbcoord[use_w] > clip_end) { \ in2 = 1; \ } \ } \ else { \ /* Point inside near plane. */ \ - if (rt->v[0]->fbcoord[use_w] < clip_start) { \ + if (tri->v[0]->fbcoord[use_w] < clip_start) { \ in0 = 1; \ } \ - if (rt->v[1]->fbcoord[use_w] < clip_start) { \ + if (tri->v[1]->fbcoord[use_w] < clip_start) { \ in1 = 1; \ } \ - if (rt->v[2]->fbcoord[use_w] < clip_start) { \ + if (tri->v[2]->fbcoord[use_w] < clip_start) { \ in2 = 1; \ } \ } @@ -1259,12 +1259,12 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) ob = reln->object_ref; for (i = 0; i < reln->element_count; i++) { /* Select the triangle in the array. */ - rt = (void *)(((uchar *)reln->pointer) + rb->triangle_size * i); + tri = (void *)(((uchar *)reln->pointer) + rb->triangle_size * i); LRT_CULL_DECIDE_INSIDE LRT_CULL_ENSURE_MEMORY lineart_triangle_cull_single(rb, - rt, + tri, in0, in1, in2, @@ -1299,20 +1299,20 @@ static void lineart_main_free_adjacent_data(LineartRenderBuffer *rb) MEM_freeN(ld->data); } LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) { - LineartTriangle *rt = reln->pointer; + LineartTriangle *tri = reln->pointer; int i; for (i = 0; i < reln->element_count; i++) { - /* See definition of rt->intersecting_verts and the usage in + /* See definition of tri->intersecting_verts and the usage in * lineart_geometry_object_load() for detailed. */ - rt->intersecting_verts = NULL; - rt = (LineartTriangle *)(((uchar *)rt) + rb->triangle_size); + tri->intersecting_verts = NULL; + tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); } } } static void lineart_main_perspective_division(LineartRenderBuffer *rb) { - LineartVert *rv; + LineartVert *vt; int i; if (!rb->cam_is_persp) { @@ -1320,18 +1320,18 @@ static void lineart_main_perspective_division(LineartRenderBuffer *rb) } LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->vertex_buffer_pointers) { - rv = reln->pointer; + vt = reln->pointer; for (i = 0; i < reln->element_count; i++) { /* Do not divide Z, we use Z to back transform cut points in later chaining process. */ - rv[i].fbcoord[0] /= rv[i].fbcoord[3]; - rv[i].fbcoord[1] /= rv[i].fbcoord[3]; + vt[i].fbcoord[0] /= vt[i].fbcoord[3]; + vt[i].fbcoord[1] /= vt[i].fbcoord[3]; /* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates) * at the moment. * The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed in * the future, the line below correctly transforms it to view space coordinates. */ - // `rv[i].fbcoord[2] = -2 * rv[i].fbcoord[2] / (far - near) - (far + near) / (far - near); - rv[i].fbcoord[0] -= rb->shift_x * 2; - rv[i].fbcoord[1] -= rb->shift_y * 2; + // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near); + vt[i].fbcoord[0] -= rb->shift_x * 2; + vt[i].fbcoord[1] -= rb->shift_y * 2; } } } @@ -1343,10 +1343,10 @@ static void lineart_vert_transform( BMVert *v, int index, LineartVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4]) { double co[4]; - LineartVert *rv = &RvBuf[index]; + LineartVert *vt = &RvBuf[index]; copy_v3db_v3fl(co, v->co); - mul_v3_m4v3_db(rv->gloc, mv_mat, co); - mul_v4_m4v3_db(rv->fbcoord, mvp_mat, co); + mul_v3_m4v3_db(vt->gloc, mv_mat, co); + mul_v4_m4v3_db(vt->fbcoord, mvp_mat, co); } /** @@ -1381,12 +1381,12 @@ static char lineart_identify_feature_line(LineartRenderBuffer *rb, return LRT_EDGE_FLAG_CONTOUR; } - LineartTriangle *rt1, *rt2; + LineartTriangle *tri1, *tri2; LineartVert *l; /* The mesh should already be triangulated now, so we can assume each face is a triangle. */ - rt1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f)); - rt2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f)); + tri1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f)); + tri2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f)); l = &rv_array[BM_elem_index_get(e->v1)]; @@ -1403,14 +1403,14 @@ static char lineart_identify_feature_line(LineartRenderBuffer *rb, view_vector = rb->view_vector; } - dot_1 = dot_v3v3_db(view_vector, rt1->gn); - dot_2 = dot_v3v3_db(view_vector, rt2->gn); + dot_1 = dot_v3v3_db(view_vector, tri1->gn); + dot_2 = dot_v3v3_db(view_vector, tri2->gn); if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { return LRT_EDGE_FLAG_CONTOUR; } - if (rb->use_crease && (dot_v3v3_db(rt1->gn, rt2->gn) < crease_threshold)) { + if (rb->use_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) { if (!no_crease) { return LRT_EDGE_FLAG_CREASE; } @@ -1448,18 +1448,18 @@ static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e) } } -static void lineart_triangle_adjacent_assign(LineartTriangle *rt, - LineartTriangleAdjacent *rta, +static void lineart_triangle_adjacent_assign(LineartTriangle *tri, + LineartTriangleAdjacent *ta, LineartEdge *e) { - if (lineart_edge_match(rt, e, 0, 1)) { - rta->e[0] = e; + if (lineart_edge_match(tri, e, 0, 1)) { + ta->e[0] = e; } - else if (lineart_edge_match(rt, e, 1, 2)) { - rta->e[1] = e; + else if (lineart_edge_match(tri, e, 1, 2)) { + ta->e[1] = e; } - else if (lineart_edge_match(rt, e, 2, 0)) { - rta->e[2] = e; + else if (lineart_edge_match(tri, e, 2, 0)) { + ta->e[2] = e; } } @@ -1477,7 +1477,7 @@ static void lineart_geometry_object_load(Depsgraph *dg, BMEdge *e; BMLoop *loop; LineartEdge *la_e; - LineartTriangle *rt; + LineartTriangle *tri; LineartTriangleAdjacent *orta; double new_mvp[4][4], new_mv[4][4], normal[4][4]; float imat[4][4]; @@ -1621,39 +1621,39 @@ static void lineart_geometry_object_load(Depsgraph *dg, * index to come close together. */ (*global_vindex) += bm->totvert; - rt = ort; + tri = ort; for (i = 0; i < bm->totface; i++) { f = BM_face_at_index(bm, i); loop = f->l_first; - rt->v[0] = &orv[BM_elem_index_get(loop->v)]; + tri->v[0] = &orv[BM_elem_index_get(loop->v)]; loop = loop->next; - rt->v[1] = &orv[BM_elem_index_get(loop->v)]; + tri->v[1] = &orv[BM_elem_index_get(loop->v)]; loop = loop->next; - rt->v[2] = &orv[BM_elem_index_get(loop->v)]; + tri->v[2] = &orv[BM_elem_index_get(loop->v)]; /* Transparency bit assignment. */ Material *mat = BKE_object_material_get(ob, f->mat_nr + 1); - rt->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ? - mat->lineart.transparency_mask : - 0); + tri->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ? + mat->lineart.transparency_mask : + 0); double gn[3]; copy_v3db_v3fl(gn, f->no); - mul_v3_mat3_m4v3_db(rt->gn, normal, gn); - normalize_v3_db(rt->gn); + mul_v3_mat3_m4v3_db(tri->gn, normal, gn); + normalize_v3_db(tri->gn); if (usage == OBJECT_LRT_INTERSECTION_ONLY) { - rt->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; + tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; } else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) { - rt->flags |= LRT_TRIANGLE_NO_INTERSECTION; + tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; } /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ - rt->intersecting_verts = (void *)&orta[i]; + tri->intersecting_verts = (void *)&orta[i]; - rt = (LineartTriangle *)(((uchar *)rt) + rb->triangle_size); + tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); } /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */ @@ -1707,9 +1707,9 @@ static void lineart_geometry_object_load(Depsgraph *dg, la_e->flags = e->head.hflag; la_e->object_ref = orig_ob; - LineartLineSegment *rls = lineart_mem_acquire(&rb->render_data_pool, - sizeof(LineartLineSegment)); - BLI_addtail(&la_e->segments, rls); + LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartEdgeSegment)); + BLI_addtail(&la_e->segments, es); if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) { lineart_add_edge_to_list(rb, la_e); } @@ -1875,51 +1875,51 @@ static void lineart_main_load_geometries( * Returns the two other verts of the triangle given a vertex. Returns false if the given vertex * doesn't belong to this triangle. */ -static bool lineart_triangle_get_other_verts(const LineartTriangle *rt, - const LineartVert *rv, +static bool lineart_triangle_get_other_verts(const LineartTriangle *tri, + const LineartVert *vt, LineartVert **l, LineartVert **r) { - if (rt->v[0] == rv) { - *l = rt->v[1]; - *r = rt->v[2]; + if (tri->v[0] == vt) { + *l = tri->v[1]; + *r = tri->v[2]; return true; } - if (rt->v[1] == rv) { - *l = rt->v[2]; - *r = rt->v[0]; + if (tri->v[1] == vt) { + *l = tri->v[2]; + *r = tri->v[0]; return true; } - if (rt->v[2] == rv) { - *l = rt->v[0]; - *r = rt->v[1]; + if (tri->v[2] == vt) { + *l = tri->v[0]; + *r = tri->v[1]; return true; } return false; } -static bool lineart_edge_from_triangle(const LineartTriangle *rt, +static bool lineart_edge_from_triangle(const LineartTriangle *tri, const LineartEdge *e, bool allow_overlapping_edges) { /* Normally we just determine from the pointer address. */ - if (e->t1 == rt || e->t2 == rt) { + if (e->t1 == tri || e->t2 == tri) { return true; } /* If allows overlapping, then we compare the vertex coordinates one by one to determine if one * edge is from specific triangle. This is slower but can handle edge split cases very well. */ if (allow_overlapping_edges) { -#define LRT_TRI_SAME_POINT(rt, i, pt) \ - ((LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \ - LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \ - LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2])) || \ - (LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \ - LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \ - LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2]))) - if ((LRT_TRI_SAME_POINT(rt, 0, e->v1) || LRT_TRI_SAME_POINT(rt, 1, e->v1) || - LRT_TRI_SAME_POINT(rt, 2, e->v1)) && - (LRT_TRI_SAME_POINT(rt, 0, e->v2) || LRT_TRI_SAME_POINT(rt, 1, e->v2) || - LRT_TRI_SAME_POINT(rt, 2, e->v2))) { +#define LRT_TRI_SAME_POINT(tri, i, pt) \ + ((LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \ + LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \ + LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])) || \ + (LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \ + LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \ + LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2]))) + if ((LRT_TRI_SAME_POINT(tri, 0, e->v1) || LRT_TRI_SAME_POINT(tri, 1, e->v1) || + LRT_TRI_SAME_POINT(tri, 2, e->v1)) && + (LRT_TRI_SAME_POINT(tri, 0, e->v2) || LRT_TRI_SAME_POINT(tri, 1, e->v2) || + LRT_TRI_SAME_POINT(tri, 2, e->v2))) { return true; } #undef LRT_TRI_SAME_POINT @@ -1961,7 +1961,7 @@ static bool lineart_edge_from_triangle(const LineartTriangle *rt, * in ratio from `e->v1` to `e->v2`. The line is later cut with these two values. */ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), - const LineartTriangle *rt, + const LineartTriangle *tri, const LineartEdge *e, const double *override_camera_loc, const bool override_cam_is_persp, @@ -1988,8 +1988,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), double gloc[4], trans[4]; double cut = -1; - double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = rt->v[0]->fbcoord, - *FBC1 = rt->v[1]->fbcoord, *FBC2 = rt->v[2]->fbcoord; + double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = tri->v[0]->fbcoord, + *FBC1 = tri->v[1]->fbcoord, *FBC2 = tri->v[2]->fbcoord; /* Overlapping not possible, return early. */ if ((MAX3(FBC0[0], FBC1[0], FBC2[0]) < MIN2(LFBC[0], RFBC[0])) || @@ -2001,7 +2001,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), } /* If the the line is one of the edge in the triangle, then it's not occluded. */ - if (lineart_edge_from_triangle(rt, e, allow_overlapping_edges)) { + if (lineart_edge_from_triangle(tri, e, allow_overlapping_edges)) { return false; } @@ -2013,8 +2013,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), /* Sort the intersection distance. */ INTERSECT_SORT_MIN_TO_MAX_3(is[0], is[1], is[2], order); - sub_v3_v3v3_db(Lv, e->v1->gloc, rt->v[0]->gloc); - sub_v3_v3v3_db(Rv, e->v2->gloc, rt->v[0]->gloc); + sub_v3_v3v3_db(Lv, e->v1->gloc, tri->v[0]->gloc); + sub_v3_v3v3_db(Rv, e->v2->gloc, tri->v[0]->gloc); copy_v3_v3_db(Cv, camera_dir); @@ -2025,12 +2025,12 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), copy_v4_v4_db(vd4, override_camera_loc); } if (override_cam_is_persp) { - sub_v3_v3v3_db(Cv, vd4, rt->v[0]->gloc); + sub_v3_v3v3_db(Cv, vd4, tri->v[0]->gloc); } - dot_l = dot_v3v3_db(Lv, rt->gn); - dot_r = dot_v3v3_db(Rv, rt->gn); - dot_f = dot_v3v3_db(Cv, rt->gn); + dot_l = dot_v3v3_db(Lv, tri->gn); + dot_r = dot_v3v3_db(Rv, tri->gn); + dot_f = dot_v3v3_db(Cv, tri->gn); if (!dot_f) { return false; @@ -2271,17 +2271,17 @@ static LineartVert *lineart_triangle_share_point(const LineartTriangle *l, /** * To save time and prevent overlapping lines when computing intersection lines. */ -static bool lineart_vert_already_intersected_2v(LineartVertIntersection *rv, +static bool lineart_vert_already_intersected_2v(LineartVertIntersection *vt, LineartVertIntersection *v1, LineartVertIntersection *v2) { - return ((rv->isec1 == v1->base.index && rv->isec2 == v2->base.index) || - (rv->isec2 == v2->base.index && rv->isec1 == v1->base.index)); + return ((vt->isec1 == v1->base.index && vt->isec2 == v2->base.index) || + (vt->isec2 == v2->base.index && vt->isec1 == v1->base.index)); } -static void lineart_vert_set_intersection_2v(LineartVert *rv, LineartVert *v1, LineartVert *v2) +static void lineart_vert_set_intersection_2v(LineartVert *vt, LineartVert *v1, LineartVert *v2) { - LineartVertIntersection *irv = (LineartVertIntersection *)rv; + LineartVertIntersection *irv = (LineartVertIntersection *)vt; irv->isec1 = v1->index; irv->isec2 = v2->index; } @@ -2294,7 +2294,7 @@ static void lineart_vert_set_intersection_2v(LineartVert *rv, LineartVert *v1, L static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *rb, LineartVert *v1, LineartVert *v2, - LineartTriangle *rt, + LineartTriangle *tri, LineartTriangle *testing, LineartVert *last) { @@ -2306,11 +2306,11 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r LineartVert *l = v1, *r = v2; for (LinkNode *ln = (void *)testing->intersecting_verts; ln; ln = ln->next) { - LineartVertIntersection *rv = ln->link; - if (rv->intersecting_with == rt && + LineartVertIntersection *vt = ln->link; + if (vt->intersecting_with == tri && lineart_vert_already_intersected_2v( - rv, (LineartVertIntersection *)l, (LineartVertIntersection *)r)) { - return (LineartVert *)rv; + vt, (LineartVertIntersection *)l, (LineartVertIntersection *)r)) { + return (LineartVert *)vt; } } @@ -2360,7 +2360,7 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r * Test if two triangles intersect. Generates one intersection line if the check succeeds. */ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, - LineartTriangle *rt, + LineartTriangle *tri, LineartTriangle *testing) { LineartVert *v1 = 0, *v2 = 0; @@ -2379,14 +2379,14 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, ZMax = rb->far_clip; ZMin = rb->near_clip; copy_v3_v3_db(cl, rb->camera_pos); - LineartVert *share = lineart_triangle_share_point(testing, rt); + LineartVert *share = lineart_triangle_share_point(testing, tri); if (share) { /* If triangles have sharing points like `abc` and `acd`, then we only need to detect `bc` * against `acd` or `cd` against `abc`. */ LineartVert *new_share; - lineart_triangle_get_other_verts(rt, share, &sv1, &sv2); + lineart_triangle_get_other_verts(tri, share, &sv1, &sv2); v1 = new_share = lineart_mem_acquire(&rb->render_data_pool, (sizeof(LineartVertIntersection))); @@ -2394,47 +2394,47 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, copy_v3_v3_db(new_share->gloc, share->gloc); - v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, rt, testing, 0); + v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, tri, testing, 0); if (v2 == NULL) { lineart_triangle_get_other_verts(testing, share, &sv1, &sv2); - v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, rt, 0); + v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, tri, 0); if (v2 == NULL) { return 0; } lineart_prepend_pool(&testing->intersecting_verts, &rb->render_data_pool, new_share); } else { - lineart_prepend_pool(&rt->intersecting_verts, &rb->render_data_pool, new_share); + lineart_prepend_pool(&tri->intersecting_verts, &rb->render_data_pool, new_share); } } else { /* If not sharing any points, then we need to try all the possibilities. */ - E0T = lineart_triangle_2v_intersection_test(rb, rt->v[0], rt->v[1], rt, testing, 0); + E0T = lineart_triangle_2v_intersection_test(rb, tri->v[0], tri->v[1], tri, testing, 0); if (E0T && (!(*next))) { (*next) = E0T; - lineart_vert_set_intersection_2v((*next), rt->v[0], rt->v[1]); + lineart_vert_set_intersection_2v((*next), tri->v[0], tri->v[1]); next = &v2; } - E1T = lineart_triangle_2v_intersection_test(rb, rt->v[1], rt->v[2], rt, testing, v1); + E1T = lineart_triangle_2v_intersection_test(rb, tri->v[1], tri->v[2], tri, testing, v1); if (E1T && (!(*next))) { (*next) = E1T; - lineart_vert_set_intersection_2v((*next), rt->v[1], rt->v[2]); + lineart_vert_set_intersection_2v((*next), tri->v[1], tri->v[2]); next = &v2; } if (!(*next)) { - E2T = lineart_triangle_2v_intersection_test(rb, rt->v[2], rt->v[0], rt, testing, v1); + E2T = lineart_triangle_2v_intersection_test(rb, tri->v[2], tri->v[0], tri, testing, v1); } if (E2T && (!(*next))) { (*next) = E2T; - lineart_vert_set_intersection_2v((*next), rt->v[2], rt->v[0]); + lineart_vert_set_intersection_2v((*next), tri->v[2], tri->v[0]); next = &v2; } if (!(*next)) { TE0 = lineart_triangle_2v_intersection_test( - rb, testing->v[0], testing->v[1], testing, rt, v1); + rb, testing->v[0], testing->v[1], testing, tri, v1); } if (TE0 && (!(*next))) { (*next) = TE0; @@ -2443,7 +2443,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, } if (!(*next)) { TE1 = lineart_triangle_2v_intersection_test( - rb, testing->v[1], testing->v[2], testing, rt, v1); + rb, testing->v[1], testing->v[2], testing, tri, v1); } if (TE1 && (!(*next))) { (*next) = TE1; @@ -2452,7 +2452,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, } if (!(*next)) { TE2 = lineart_triangle_2v_intersection_test( - rb, testing->v[2], testing->v[0], testing, rt, v1); + rb, testing->v[2], testing->v[0], testing, tri, v1); } if (TE2 && (!(*next))) { (*next) = TE2; @@ -2484,17 +2484,17 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin)); v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin)); - ((LineartVertIntersection *)v1)->intersecting_with = rt; + ((LineartVertIntersection *)v1)->intersecting_with = tri; ((LineartVertIntersection *)v2)->intersecting_with = testing; result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge)); result->v1 = v1; result->v2 = v2; - result->t1 = rt; + result->t1 = tri; result->t2 = testing; - LineartLineSegment *rls = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartLineSegment)); - BLI_addtail(&result->segments, rls); + LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); + BLI_addtail(&result->segments, es); /* Don't need to OR flags right now, just a type mark. */ result->flags = LRT_EDGE_FLAG_INTERSECTION; lineart_prepend_edge_direct(&rb->intersection_lines, result); @@ -2502,7 +2502,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, if (lineart_get_edge_bounding_areas(rb, result, &r1, &r2, &c1, &c2)) { for (row = r1; row != r2 + 1; row++) { for (col = c1; col != c2 + 1; col++) { - lineart_bounding_area_link_line( + lineart_bounding_area_link_edge( rb, &rb->initial_bounding_areas[row * LRT_BA_ROWS + col], result); } } @@ -2514,23 +2514,23 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, } static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, - LineartTriangle *rt, + LineartTriangle *tri, LineartBoundingArea *ba) { /* Testing_triangle->testing[0] is used to store pairing triangle reference. * See definition of LineartTriangleThread for more info. */ LineartTriangle *testing_triangle; - LineartTriangleThread *rtt; + LineartTriangleThread *tt; LinkData *lip, *next_lip; - double *G0 = rt->v[0]->gloc, *G1 = rt->v[1]->gloc, *G2 = rt->v[2]->gloc; + double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc; /* If this is not the smallest subdiv bounding area.*/ if (ba->child) { - lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[0]); - lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[1]); - lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[2]); - lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[3]); + lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[0]); + lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[1]); + lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[2]); + lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[3]); return; } @@ -2538,16 +2538,16 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, for (lip = ba->linked_triangles.first; lip; lip = next_lip) { next_lip = lip->next; testing_triangle = lip->data; - rtt = (LineartTriangleThread *)testing_triangle; + tt = (LineartTriangleThread *)testing_triangle; - if (testing_triangle == rt || rtt->testing_e[0] == (LineartEdge *)rt) { + if (testing_triangle == tri || tt->testing_e[0] == (LineartEdge *)tri) { continue; } - rtt->testing_e[0] = (LineartEdge *)rt; + tt->testing_e[0] = (LineartEdge *)tri; if ((testing_triangle->flags & LRT_TRIANGLE_NO_INTERSECTION) || ((testing_triangle->flags & LRT_TRIANGLE_INTERSECTION_ONLY) && - (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY))) { + (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY))) { continue; } @@ -2561,12 +2561,12 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, (MAX3(G0[0], G1[0], G2[0]) < MIN3(RG0[0], RG1[0], RG2[0])) || (MIN3(G0[1], G1[1], G2[1]) > MAX3(RG0[1], RG1[1], RG2[1])) || (MAX3(G0[1], G1[1], G2[1]) < MIN3(RG0[1], RG1[1], RG2[1])) || - lineart_triangle_share_edge(rt, testing_triangle)) { + lineart_triangle_share_edge(tri, testing_triangle)) { continue; } /* If we do need to compute intersection, then finally do it. */ - lineart_triangle_intersect(rb, rt, testing_triangle); + lineart_triangle_intersect(rb, tri, testing_triangle); } } @@ -2925,7 +2925,7 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb, { LineartBoundingArea *ba = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartBoundingArea) * 4); - LineartTriangle *rt; + LineartTriangle *tri; LineartEdge *e; ba[0].l = root->cx; @@ -2960,35 +2960,35 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb, lineart_bounding_areas_connect_new(rb, root); - while ((rt = lineart_list_pop_pointer_no_free(&root->linked_triangles)) != NULL) { + while ((tri = lineart_list_pop_pointer_no_free(&root->linked_triangles)) != NULL) { LineartBoundingArea *cba = root->child; double b[4]; - b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); - b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); + b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); + b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); if (LRT_BOUND_AREA_CROSSES(b, &cba[0].l)) { - lineart_bounding_area_link_triangle(rb, &cba[0], rt, b, 0, recursive_level + 1, false); + lineart_bounding_area_link_triangle(rb, &cba[0], tri, b, 0, recursive_level + 1, false); } if (LRT_BOUND_AREA_CROSSES(b, &cba[1].l)) { - lineart_bounding_area_link_triangle(rb, &cba[1], rt, b, 0, recursive_level + 1, false); + lineart_bounding_area_link_triangle(rb, &cba[1], tri, b, 0, recursive_level + 1, false); } if (LRT_BOUND_AREA_CROSSES(b, &cba[2].l)) { - lineart_bounding_area_link_triangle(rb, &cba[2], rt, b, 0, recursive_level + 1, false); + lineart_bounding_area_link_triangle(rb, &cba[2], tri, b, 0, recursive_level + 1, false); } if (LRT_BOUND_AREA_CROSSES(b, &cba[3].l)) { - lineart_bounding_area_link_triangle(rb, &cba[3], rt, b, 0, recursive_level + 1, false); + lineart_bounding_area_link_triangle(rb, &cba[3], tri, b, 0, recursive_level + 1, false); } } - while ((e = lineart_list_pop_pointer_no_free(&root->linked_lines)) != NULL) { - lineart_bounding_area_link_line(rb, root, e); + while ((e = lineart_list_pop_pointer_no_free(&root->linked_edges)) != NULL) { + lineart_bounding_area_link_edge(rb, root, e); } rb->bounding_area_count += 3; } -static bool lineart_bounding_area_line_intersect(LineartRenderBuffer *UNUSED(fb), +static bool lineart_bounding_area_edge_intersect(LineartRenderBuffer *UNUSED(fb), const double l[2], const double r[2], LineartBoundingArea *ba) @@ -3032,11 +3032,11 @@ static bool lineart_bounding_area_line_intersect(LineartRenderBuffer *UNUSED(fb) } static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, - LineartTriangle *rt, + LineartTriangle *tri, LineartBoundingArea *ba) { double p1[2], p2[2], p3[2], p4[2]; - double *FBC1 = rt->v[0]->fbcoord, *FBC2 = rt->v[1]->fbcoord, *FBC3 = rt->v[2]->fbcoord; + double *FBC1 = tri->v[0]->fbcoord, *FBC2 = tri->v[1]->fbcoord, *FBC3 = tri->v[2]->fbcoord; p3[0] = p1[0] = (double)ba->l; p2[1] = p1[1] = (double)ba->b; @@ -3056,9 +3056,9 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, return true; } - if ((lineart_bounding_area_line_intersect(fb, FBC1, FBC2, ba)) || - (lineart_bounding_area_line_intersect(fb, FBC2, FBC3, ba)) || - (lineart_bounding_area_line_intersect(fb, FBC3, FBC1, ba))) { + if ((lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba)) || + (lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba)) || + (lineart_bounding_area_edge_intersect(fb, FBC3, FBC1, ba))) { return true; } @@ -3071,17 +3071,17 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, */ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, LineartBoundingArea *root_ba, - LineartTriangle *rt, + LineartTriangle *tri, double *LRUB, int recursive, int recursive_level, bool do_intersection) { - if (!lineart_bounding_area_triangle_intersect(rb, rt, root_ba)) { + if (!lineart_bounding_area_triangle_intersect(rb, tri, root_ba)) { return; } if (root_ba->child == NULL) { - lineart_list_append_pointer_pool(&root_ba->linked_triangles, &rb->render_data_pool, rt); + lineart_list_append_pointer_pool(&root_ba->linked_triangles, &rb->render_data_pool, tri); root_ba->triangle_count++; /* If splitting doesn't improve triangle separation, then shouldn't allow splitting anymore. * Here we use recursive limit. This is especially useful in orthographic render, @@ -3091,7 +3091,7 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, lineart_bounding_area_split(rb, root_ba, recursive_level); } if (recursive && do_intersection && rb->use_intersections) { - lineart_triangle_intersect_in_bounding_area(rb, rt, root_ba); + lineart_triangle_intersect_in_bounding_area(rb, tri, root_ba); } } else { @@ -3099,54 +3099,54 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, double *B1 = LRUB; double b[4]; if (!LRUB) { - b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); - b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); + b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); + b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); B1 = b; } if (LRT_BOUND_AREA_CROSSES(B1, &ba[0].l)) { lineart_bounding_area_link_triangle( - rb, &ba[0], rt, B1, recursive, recursive_level + 1, do_intersection); + rb, &ba[0], tri, B1, recursive, recursive_level + 1, do_intersection); } if (LRT_BOUND_AREA_CROSSES(B1, &ba[1].l)) { lineart_bounding_area_link_triangle( - rb, &ba[1], rt, B1, recursive, recursive_level + 1, do_intersection); + rb, &ba[1], tri, B1, recursive, recursive_level + 1, do_intersection); } if (LRT_BOUND_AREA_CROSSES(B1, &ba[2].l)) { lineart_bounding_area_link_triangle( - rb, &ba[2], rt, B1, recursive, recursive_level + 1, do_intersection); + rb, &ba[2], tri, B1, recursive, recursive_level + 1, do_intersection); } if (LRT_BOUND_AREA_CROSSES(B1, &ba[3].l)) { lineart_bounding_area_link_triangle( - rb, &ba[3], rt, B1, recursive, recursive_level + 1, do_intersection); + rb, &ba[3], tri, B1, recursive, recursive_level + 1, do_intersection); } } } -static void lineart_bounding_area_link_line(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, LineartBoundingArea *root_ba, LineartEdge *e) { if (root_ba->child == NULL) { - lineart_list_append_pointer_pool(&root_ba->linked_lines, &rb->render_data_pool, e); + lineart_list_append_pointer_pool(&root_ba->linked_edges, &rb->render_data_pool, e); } else { - if (lineart_bounding_area_line_intersect( + if (lineart_bounding_area_edge_intersect( rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0])) { - lineart_bounding_area_link_line(rb, &root_ba->child[0], e); + lineart_bounding_area_link_edge(rb, &root_ba->child[0], e); } - if (lineart_bounding_area_line_intersect( + if (lineart_bounding_area_edge_intersect( rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1])) { - lineart_bounding_area_link_line(rb, &root_ba->child[1], e); + lineart_bounding_area_link_edge(rb, &root_ba->child[1], e); } - if (lineart_bounding_area_line_intersect( + if (lineart_bounding_area_edge_intersect( rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2])) { - lineart_bounding_area_link_line(rb, &root_ba->child[2], e); + lineart_bounding_area_link_edge(rb, &root_ba->child[2], e); } - if (lineart_bounding_area_line_intersect( + if (lineart_bounding_area_edge_intersect( rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3])) { - lineart_bounding_area_link_line(rb, &root_ba->child[3], e); + lineart_bounding_area_link_edge(rb, &root_ba->child[3], e); } } } @@ -3162,7 +3162,7 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb) if (lineart_get_edge_bounding_areas(rb, e, &r1, &r2, &c1, &c2)) { for (row = r1; row != r2 + 1; row++) { for (col = c1; col != c2 + 1; col++) { - lineart_bounding_area_link_line( + lineart_bounding_area_link_edge( rb, &rb->initial_bounding_areas[row * LRT_BA_ROWS + col], e); } } @@ -3172,7 +3172,7 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb) } static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, - LineartTriangle *rt, + LineartTriangle *tri, int *rowbegin, int *rowend, int *colbegin, @@ -3181,14 +3181,14 @@ static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; double b[4]; - if (!rt->v[0] || !rt->v[1] || !rt->v[2]) { + if (!tri->v[0] || !tri->v[1] || !tri->v[2]) { return false; } - b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]); - b[2] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); - b[3] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]); + b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); + b[2] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); + b[3] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) { return false; @@ -3355,33 +3355,33 @@ LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, doub */ static void lineart_main_add_triangles(LineartRenderBuffer *rb) { - LineartTriangle *rt; + LineartTriangle *tri; int i, lim; int x1, x2, y1, y2; int r, co; LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) { - rt = reln->pointer; + tri = reln->pointer; lim = reln->element_count; for (i = 0; i < lim; i++) { - if ((rt->flags & LRT_CULL_USED) || (rt->flags & LRT_CULL_DISCARD)) { - rt = (void *)(((uchar *)rt) + rb->triangle_size); + if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) { + tri = (void *)(((uchar *)tri) + rb->triangle_size); continue; } - if (lineart_get_triangle_bounding_areas(rb, rt, &y1, &y2, &x1, &x2)) { + if (lineart_get_triangle_bounding_areas(rb, tri, &y1, &y2, &x1, &x2)) { for (co = x1; co <= x2; co++) { for (r = y1; r <= y2; r++) { lineart_bounding_area_link_triangle(rb, &rb->initial_bounding_areas[r * LRT_BA_ROWS + co], - rt, + tri, 0, 1, 0, - (!(rt->flags & LRT_TRIANGLE_NO_INTERSECTION))); + (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION))); } } } /* Else throw away. */ - rt = (void *)(((uchar *)rt) + rb->triangle_size); + tri = (void *)(((uchar *)tri) + rb->triangle_size); } } } @@ -3817,52 +3817,52 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP; bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP; - LISTBASE_FOREACH (LineartLineChain *, rlc, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { - if (rlc->picked) { + if (ec->picked) { continue; } - if (!(rlc->type & (types & enabled_types))) { + if (!(ec->type & (types & enabled_types))) { continue; } - if (rlc->level > level_end || rlc->level < level_start) { + if (ec->level > level_end || ec->level < level_start) { continue; } - if (orig_ob && orig_ob != rlc->object_ref) { + if (orig_ob && orig_ob != ec->object_ref) { continue; } - if (orig_col && rlc->object_ref) { - if (!BKE_collection_has_object_recursive_instanced(orig_col, (Object *)rlc->object_ref)) { + if (orig_col && ec->object_ref) { + if (!BKE_collection_has_object_recursive_instanced(orig_col, (Object *)ec->object_ref)) { continue; } } if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_ENABLE) { if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_MATCH) { - if (rlc->transparency_mask != transparency_mask) { + if (ec->transparency_mask != transparency_mask) { continue; } } else { - if (!(rlc->transparency_mask & transparency_mask)) { + if (!(ec->transparency_mask & transparency_mask)) { continue; } } } /* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */ - // rlc->picked = 1; + // ec->picked = 1; int array_idx = 0; - int count = MOD_lineart_chain_count(rlc); + int count = MOD_lineart_chain_count(ec); bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false); float *stroke_data = MEM_callocN(sizeof(float) * count * GP_PRIM_DATABUF_SIZE, "line art add stroke"); - LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) { - stroke_data[array_idx] = rlci->gpos[0]; - stroke_data[array_idx + 1] = rlci->gpos[1]; - stroke_data[array_idx + 2] = rlci->gpos[2]; + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + stroke_data[array_idx] = eci->gpos[0]; + stroke_data[array_idx + 1] = eci->gpos[1]; + stroke_data[array_idx + 2] = eci->gpos[2]; mul_m4_v3(gp_obmat_inverse, &stroke_data[array_idx]); stroke_data[array_idx + 3] = 1; /* thickness. */ stroke_data[array_idx + 4] = opacity; /* hardness?. */ @@ -3876,7 +3876,7 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, MEM_freeN(stroke_data); if (source_vgname && vgname) { - Object *eval_ob = DEG_get_evaluated_object(depsgraph, rlc->object_ref); + Object *eval_ob = DEG_get_evaluated_object(depsgraph, ec->object_ref); int gpdg = -1; if ((match_output || (gpdg = BKE_object_defgroup_name_index(gpencil_object, vgname)) >= 0)) { if (eval_ob && eval_ob->type == OB_MESH) { @@ -3892,8 +3892,8 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb, } } int sindex = 0, vindex; - LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) { - vindex = rlci->index; + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + vindex = eci->index; if (vindex >= me->totvert) { break; } -- cgit v1.2.3 From 8cd506639a789694bb229698adad6de36bc1a26d Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Mon, 24 May 2021 12:14:06 +0100 Subject: Geometry Nodes: Add Shader Curve Nodes Convert curve vec and curve rgb shader nodes to geometry nodes, based on node_shader_valToRgb.cc implementation. --- release/scripts/startup/nodeitems_builtins.py | 2 + source/blender/blenkernel/intern/node.cc | 2 +- .../nodes/shader/nodes/node_shader_curves.cc | 100 ++++++++++++++++++++- 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index c900bbb074c..4bff18cd1be 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -495,6 +495,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeAttributeTransfer"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ + NodeItem("ShaderNodeRGBCurve"), NodeItem("ShaderNodeValToRGB"), NodeItem("ShaderNodeSeparateRGB"), NodeItem("ShaderNodeCombineRGB"), @@ -557,6 +558,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeSwitch"), ]), GeometryNodeCategory("GEO_VECTOR", "Vector", items=[ + NodeItem("ShaderNodeVectorCurve"), NodeItem("ShaderNodeSeparateXYZ"), NodeItem("ShaderNodeCombineXYZ"), NodeItem("ShaderNodeVectorMath"), diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 643dc58af18..3377f5c69dc 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -513,7 +513,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) if (node->storage) { /* could be handlerized at some point, now only 1 exception still */ - if ((ntree->type == NTREE_SHADER) && + if ((ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY)) && ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 551c1e4e8c4..bc99e4b5dd8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -104,17 +104,65 @@ static int gpu_shader_curve_vec(GPUMaterial *mat, GPU_uniform(ext_xyz[2])); } +class CurveVecFunction : public blender::fn::MultiFunction { + private: + const CurveMapping &cumap_; + + public: + CurveVecFunction(const CurveMapping &cumap) : cumap_(cumap) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Curve Vec"}; + signature.single_input("Fac"); + signature.single_input("Vector"); + signature.single_output("Vector"); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray &fac = params.readonly_single_input(0, "Fac"); + const blender::VArray &vec_in = params.readonly_single_input( + 1, "Vector"); + blender::MutableSpan vec_out = + params.uninitialized_single_output(2, "Vector"); + + for (int64_t i : mask) { + BKE_curvemapping_evaluate3F(&cumap_, vec_out[i], vec_in[i]); + if (fac[i] != 1.0f) { + interp_v3_v3v3(vec_out[i], vec_in[i], vec_out[i], fac[i]); + } + } + } +}; + +static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +{ + bNode &bnode = builder.bnode(); + CurveMapping *cumap = (CurveMapping *)bnode.storage; + BKE_curvemapping_init(cumap); + builder.construct_and_set_matching_fn(*cumap); +} + void register_node_type_sh_curve_vec(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); node_type_socket_templates(&ntype, sh_node_curve_vec_in, sh_node_curve_vec_out); node_type_init(&ntype, node_shader_init_curve_vec); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec); node_type_gpu(&ntype, gpu_shader_curve_vec); + ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network; nodeRegisterType(&ntype); } @@ -230,17 +278,65 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat, GPU_uniform(ext_rgba[3])); } +class CurveRGBFunction : public blender::fn::MultiFunction { + private: + const CurveMapping &cumap_; + + public: + CurveRGBFunction(const CurveMapping &cumap) : cumap_(cumap) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Curve RGB"}; + signature.single_input("Fac"); + signature.single_input("Color"); + signature.single_output("Color"); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray &fac = params.readonly_single_input(0, "Fac"); + const blender::VArray &col_in = + params.readonly_single_input(1, "Color"); + blender::MutableSpan col_out = + params.uninitialized_single_output(2, "Color"); + + for (int64_t i : mask) { + BKE_curvemapping_evaluateRGBF(&cumap_, col_out[i], col_in[i]); + if (fac[i] != 1.0f) { + interp_v3_v3v3(col_out[i], col_in[i], col_out[i], fac[i]); + } + } + } +}; + +static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +{ + bNode &bnode = builder.bnode(); + CurveMapping *cumap = (CurveMapping *)bnode.storage; + BKE_curvemapping_init(cumap); + builder.construct_and_set_matching_fn(*cumap); +} + void register_node_type_sh_curve_rgb(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); node_type_socket_templates(&ntype, sh_node_curve_rgb_in, sh_node_curve_rgb_out); node_type_init(&ntype, node_shader_init_curve_rgb); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb); node_type_gpu(&ntype, gpu_shader_curve_rgb); + ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network; nodeRegisterType(&ntype); } -- cgit v1.2.3 From b046bc536bec914013c678b552ce6cef7dd308e6 Mon Sep 17 00:00:00 2001 From: Patrick Mours Date: Tue, 25 May 2021 16:56:16 +0200 Subject: Fix T88096: Baking with OptiX and displacement fails Using displacement runs the shader eval kernel, but since OptiX modules are not loaded when baking is active, those were not available and therefore failed to launch. This fixes that by falling back to the CUDA kernels. --- intern/cycles/device/device_optix.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index 01de0724cb2..b008dfa376f 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -726,7 +726,11 @@ class OptiXDevice : public CUDADevice { } } else if (task.type == DeviceTask::SHADER) { - launch_shader_eval(task, thread_index); + // CUDA kernels are used when doing baking + if (optix_module == NULL) + CUDADevice::shader(task); + else + launch_shader_eval(task, thread_index); } else if (task.type == DeviceTask::DENOISE_BUFFER) { // Set up a single tile that covers the whole task and denoise it -- cgit v1.2.3 From fd94e033446c72fb92048a9864c1d539fccde59a Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 25 May 2021 17:00:14 +0200 Subject: Blenlib: Explicit Colors. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Colors are often thought of as being 4 values that make up that can make any color. But that is of course too limited. In C we didn’t spend time to annotate what we meant when using colors. Recently `BLI_color.hh` was made to facilitate color structures in CPP. CPP has possibilities to enforce annotating structures during compilation and can adds conversions between them using function overloading and explicit constructors. The storage structs can hold 4 channels (r, g, b and a). Usage: Convert a theme byte color to a linearrgb premultiplied. ``` ColorTheme4b theme_color; ColorSceneLinear4f linearrgb_color = BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha(); ``` The API is structured to make most use of inlining. Most notable are space conversions done via `BLI_color_convert_to*` functions. - Conversions between spaces (theme <=> scene linear) should always be done by invoking the `BLI_color_convert_to*` methods. - Encoding colors (compressing to store colors inside a less precision storage) should be done by invoking the `encode` and `decode` methods. - Changing alpha association should be done by invoking `premultiply_alpha` or `unpremultiply_alpha` methods. # Encoding. Color encoding is used to store colors with less precision as in using `uint8_t` in stead of `float`. This encoding is supported for `eSpace::SceneLinear`. To make this clear to the developer the `eSpace::SceneLinearByteEncoded` space is added. # Precision Colors can be stored using `uint8_t` or `float` colors. The conversion between the two precisions are available as methods. (`to_4b` and `to_4f`). # Alpha conversion Alpha conversion is only supported in SceneLinear space. Extending: - This file can be extended with `ColorHex/Hsl/Hsv` for different representations of rgb based colors. `ColorHsl4f` - Add non RGB spaces/storages ColorXyz. Reviewed By: JacquesLucke, brecht Differential Revision: https://developer.blender.org/D10978 --- source/blender/blenkernel/BKE_attribute_math.hh | 35 ++- .../blender/blenkernel/intern/attribute_access.cc | 8 +- source/blender/blenkernel/intern/attribute_math.cc | 15 +- .../blenkernel/intern/geometry_component_mesh.cc | 20 +- source/blender/blenlib/BLI_color.hh | 302 ++++++++++++++++++--- source/blender/blenlib/CMakeLists.txt | 2 + source/blender/blenlib/intern/BLI_color.cc | 55 ++++ source/blender/blenlib/tests/BLI_color_test.cc | 133 +++++++++ source/blender/draw/intern/draw_cache_impl.h | 1 - .../space_spreadsheet/spreadsheet_cell_value.hh | 2 +- .../spreadsheet_data_source_geometry.cc | 2 +- .../space_spreadsheet/spreadsheet_layout.cc | 2 +- source/blender/functions/intern/cpp_types.cc | 4 +- .../geometry/nodes/node_geo_attribute_clamp.cc | 16 +- .../nodes/node_geo_attribute_color_ramp.cc | 6 +- .../geometry/nodes/node_geo_attribute_compare.cc | 28 +- .../geometry/nodes/node_geo_attribute_curve_map.cc | 7 +- .../geometry/nodes/node_geo_attribute_fill.cc | 2 +- .../nodes/geometry/nodes/node_geo_attribute_mix.cc | 16 +- .../nodes/node_geo_attribute_sample_texture.cc | 7 +- .../nodes/geometry/nodes/node_geo_switch.cc | 2 +- source/blender/nodes/intern/node_geometry_exec.cc | 5 +- source/blender/nodes/intern/node_socket.cc | 4 +- source/blender/nodes/intern/type_conversions.cc | 50 ++-- .../nodes/shader/nodes/node_shader_sepcombRGB.cc | 13 +- .../nodes/shader/nodes/node_shader_valToRgb.cc | 8 +- 26 files changed, 594 insertions(+), 151 deletions(-) create mode 100644 source/blender/blenlib/intern/BLI_color.cc create mode 100644 source/blender/blenlib/tests/BLI_color_test.cc diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 0afdc436415..ba683362e69 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -52,7 +52,7 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f func(bool()); break; case CD_PROP_COLOR: - func(Color4f()); + func(ColorGeometry4f()); break; default: BLI_assert_unreachable(); @@ -78,8 +78,8 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func else if (cpp_type.is()) { func(bool()); } - else if (cpp_type.is()) { - func(Color4f()); + else if (cpp_type.is()) { + func(ColorGeometry4f()); } else { BLI_assert_unreachable(); @@ -123,9 +123,12 @@ inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, co } template<> -inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1, const Color4f &v2) +inline ColorGeometry4f mix3(const float3 &weights, + const ColorGeometry4f &v0, + const ColorGeometry4f &v1, + const ColorGeometry4f &v2) { - Color4f result; + ColorGeometry4f result; interp_v4_v4v4v4(result, v0, v1, v2, weights); return result; } @@ -165,9 +168,10 @@ template<> inline float3 mix2(const float factor, const float3 &a, const float3 return float3::interpolate(a, b, factor); } -template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b) +template<> +inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b) { - Color4f result; + ColorGeometry4f result; interp_v4_v4v4(result, a, b, factor); return result; } @@ -274,15 +278,16 @@ class SimpleMixerWithAccumulationType { } }; -class Color4fMixer { +class ColorGeometryMixer { private: - MutableSpan buffer_; - Color4f default_color_; + MutableSpan buffer_; + ColorGeometry4f default_color_; Array total_weights_; public: - Color4fMixer(MutableSpan buffer, Color4f default_color = {0, 0, 0, 1}); - void mix_in(const int64_t index, const Color4f &color, const float weight = 1.0f); + ColorGeometryMixer(MutableSpan buffer, + ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + void mix_in(const int64_t index, const ColorGeometry4f &color, const float weight = 1.0f); void finalize(); }; @@ -299,10 +304,10 @@ template<> struct DefaultMixerStruct { template<> struct DefaultMixerStruct { using type = SimpleMixer; }; -template<> struct DefaultMixerStruct { - /* Use a special mixer for colors. Color4f can't be added/multiplied, because this is not +template<> struct DefaultMixerStruct { + /* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not * something one should usually do with colors. */ - using type = Color4fMixer; + using type = ColorGeometryMixer; }; template<> struct DefaultMixerStruct { static int double_to_int(const double &value) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 62833e10438..d36e9ed3e86 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -61,7 +61,7 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty case CD_PROP_INT32: return &CPPType::get(); case CD_PROP_COLOR: - return &CPPType::get(); + return &CPPType::get(); case CD_PROP_BOOL: return &CPPType::get(); default: @@ -84,7 +84,7 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) if (type.is()) { return CD_PROP_INT32; } - if (type.is()) { + if (type.is()) { return CD_PROP_COLOR; } if (type.is()) { @@ -355,7 +355,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( case CD_PROP_INT32: return this->layer_to_read_attribute(layer, domain_size); case CD_PROP_COLOR: - return this->layer_to_read_attribute(layer, domain_size); + return this->layer_to_read_attribute(layer, domain_size); case CD_PROP_BOOL: return this->layer_to_read_attribute(layer, domain_size); default: @@ -389,7 +389,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( case CD_PROP_INT32: return this->layer_to_write_attribute(layer, domain_size); case CD_PROP_COLOR: - return this->layer_to_write_attribute(layer, domain_size); + return this->layer_to_write_attribute(layer, domain_size); case CD_PROP_BOOL: return this->layer_to_write_attribute(layer, domain_size); default: diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc index 4ff3a6ceff5..5cdf329effb 100644 --- a/source/blender/blenkernel/intern/attribute_math.cc +++ b/source/blender/blenkernel/intern/attribute_math.cc @@ -18,18 +18,21 @@ namespace blender::attribute_math { -Color4fMixer::Color4fMixer(MutableSpan output_buffer, Color4f default_color) +ColorGeometryMixer::ColorGeometryMixer(MutableSpan output_buffer, + ColorGeometry4f default_color) : buffer_(output_buffer), default_color_(default_color), total_weights_(output_buffer.size(), 0.0f) { - buffer_.fill(Color4f(0, 0, 0, 0)); + buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); } -void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float weight) +void ColorGeometryMixer::mix_in(const int64_t index, + const ColorGeometry4f &color, + const float weight) { BLI_assert(weight >= 0.0f); - Color4f &output_color = buffer_[index]; + ColorGeometry4f &output_color = buffer_[index]; output_color.r += color.r * weight; output_color.g += color.g * weight; output_color.b += color.b * weight; @@ -37,11 +40,11 @@ void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float total_weights_[index] += weight; } -void Color4fMixer::finalize() +void ColorGeometryMixer::finalize() { for (const int64_t i : buffer_.index_range()) { const float weight = total_weights_[i]; - Color4f &output_color = buffer_[i]; + ColorGeometry4f &output_color = buffer_[i]; if (weight > 0.0f) { const float weight_inv = 1.0f / weight; output_color.r *= weight_inv; diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 9e622ab2cdf..42f3a854aec 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -773,18 +773,20 @@ static void set_loop_uv(MLoopUV &uv, float2 co) copy_v2_v2(uv.uv, co); } -static Color4f get_loop_color(const MLoopCol &col) +static ColorGeometry4f get_loop_color(const MLoopCol &col) { - Color4f srgb_color; - rgba_uchar_to_float(srgb_color, &col.r); - Color4f linear_color; - srgb_to_linearrgb_v4(linear_color, srgb_color); + ColorGeometry4b encoded_color = ColorGeometry4b(col.r, col.g, col.b, col.a); + ColorGeometry4f linear_color = encoded_color.decode(); return linear_color; } -static void set_loop_color(MLoopCol &col, Color4f linear_color) +static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color) { - linearrgb_to_srgb_uchar4(&col.r, linear_color); + ColorGeometry4b encoded_color = linear_color.encode(); + col.r = encoded_color.r; + col.g = encoded_color.g; + col.b = encoded_color.b; + col.a = encoded_color.a; } static float get_crease(const MEdge &edge) @@ -1121,8 +1123,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_COLOR, CD_MLOOPCOL, corner_access, - make_derived_read_attribute, - make_derived_write_attribute); + make_derived_read_attribute, + make_derived_write_attribute); static VertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index e57a5109a66..287587e04be 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -22,41 +22,122 @@ namespace blender { -struct Color4f { - float r, g, b, a; +/** + * CPP based color structures. + * + * Strongly typed color storage structures with space and alpha association. + * Will increase readability and visibility of typical mistakes when + * working with colors. + * + * The storage structs can hold 4 channels (r, g, b and a). + * + * Usage: + * + * Convert a theme byte color to a linearrgb premultiplied. + * ``` + * ColorTheme4b theme_color; + * ColorSceneLinear4f linearrgb_color = + * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha(); + * ``` + * + * The API is structured to make most use of inlining. Most notable are space + * conversions done via `BLI_color_convert_to*` functions. + * + * - Conversions between spaces (theme <=> scene linear) should always be done by + * invoking the `BLI_color_convert_to*` methods. + * - Encoding colors (compressing to store colors inside a less precision storage) + * should be done by invoking the `encode` and `decode` methods. + * - Changing alpha association should be done by invoking `premultiply_alpha` or + * `unpremultiply_alpha` methods. + * + * # Encoding. + * + * Color encoding is used to store colors with less precision as in using `uint8_t` in + * stead of `float`. This encoding is supported for `eSpace::SceneLinear`. + * To make this clear to the developer the `eSpace::SceneLinearByteEncoded` + * space is added. + * + * # Precision + * + * Colors can be stored using `uint8_t` or `float` colors. The conversion + * between the two precisions are available as methods. (`to_4b` and + * `to_4f`). + * + * # Alpha conversion + * + * Alpha conversion is only supported in SceneLinear space. + * + * Extending this file: + * - This file can be extended with `ColorHex/Hsl/Hsv` for different representations + * of rgb based colors. `ColorHsl4f` + * - Add non RGB spaces/storages ColorXyz. + */ + +/* Enumeration containing the different alpha modes. */ +enum class eAlpha { + /* Color and alpha are unassociated. */ + Straight, + /* Color and alpha are associated. */ + Premultiplied, +}; +std::ostream &operator<<(std::ostream &stream, const eAlpha &space); - Color4f() = default; +/* Enumeration containing internal spaces. */ +enum class eSpace { + /* Blender theme color space (sRGB). */ + Theme, + /* Blender internal scene linear color space (maps to SceneReference role in OCIO). */ + SceneLinear, + /* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */ + SceneLinearByteEncoded, +}; +std::ostream &operator<<(std::ostream &stream, const eSpace &space); - Color4f(const float *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) +/* Template class to store RGBA values with different precision, space and alpha association. */ +template class ColorRGBA { + public: + ChannelStorageType r, g, b, a; + constexpr ColorRGBA() = default; + + constexpr ColorRGBA(const ChannelStorageType rgba[4]) + : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) { } - Color4f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) + constexpr ColorRGBA(const ChannelStorageType r, + const ChannelStorageType g, + const ChannelStorageType b, + const ChannelStorageType a) + : r(r), g(g), b(b), a(a) { } - operator float *() + operator ChannelStorageType *() { return &r; } - operator const float *() const + operator const ChannelStorageType *() const { return &r; } - friend std::ostream &operator<<(std::ostream &stream, Color4f c) + friend std::ostream &operator<<(std::ostream &stream, + const ColorRGBA &c) { - stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; + + stream << Space << Alpha << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; return stream; } - friend bool operator==(const Color4f &a, const Color4f &b) + friend bool operator==(const ColorRGBA &a, + const ColorRGBA &b) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - friend bool operator!=(const Color4f &a, const Color4f &b) + friend bool operator!=(const ColorRGBA &a, + const ColorRGBA &b) { return !(a == b); } @@ -71,58 +152,209 @@ struct Color4f { } }; -struct Color4b { - uint8_t r, g, b, a; +/* Forward declarations of concrete color classes. */ +template class ColorSceneLinear4f; +template class ColorSceneLinearByteEncoded4b; +template class ColorTheme4; - Color4b() = default; +/* Forward declation of precision conversion methods. */ +BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4f(const ColorTheme4 &srgb4b); +BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4b(const ColorTheme4 &srgb4f); - Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) +template +class ColorSceneLinear4f final : public ColorRGBA { + public: + constexpr ColorSceneLinear4f() : ColorRGBA() { } - Color4b(Color4f other) + constexpr ColorSceneLinear4f(const float *rgba) + : ColorRGBA(rgba) { - rgba_float_to_uchar(*this, other); } - operator Color4f() const + constexpr ColorSceneLinear4f(float r, float g, float b, float a) + : ColorRGBA(r, g, b, a) { - Color4f result; - rgba_uchar_to_float(result, *this); - return result; } - operator uint8_t *() + /** + * Convert to its byte encoded counter space. + **/ + ColorSceneLinearByteEncoded4b encode() const { - return &r; + ColorSceneLinearByteEncoded4b encoded; + linearrgb_to_srgb_uchar4(encoded, *this); + return encoded; } - operator const uint8_t *() const + /** + * Convert color and alpha association to premultiplied alpha. + * + * Does nothing when color has already a premultiplied alpha. + */ + ColorSceneLinear4f premultiply_alpha() const { - return &r; + if constexpr (Alpha == eAlpha::Straight) { + ColorSceneLinear4f premultiplied; + straight_to_premul_v4_v4(premultiplied, *this); + return premultiplied; + } + else { + return *this; + } } - friend std::ostream &operator<<(std::ostream &stream, Color4b c) + /** + * Convert color and alpha association to straight alpha. + * + * Does nothing when color has straighten alpha. + */ + ColorSceneLinear4f unpremultiply_alpha() const { - stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; - return stream; + if constexpr (Alpha == eAlpha::Premultiplied) { + ColorSceneLinear4f straighten; + premul_to_straight_v4_v4(straighten, *this); + return straighten; + } + else { + return *this; + } } +}; - friend bool operator==(const Color4b &a, const Color4b &b) +template +class ColorSceneLinearByteEncoded4b final + : public ColorRGBA { + public: + constexpr ColorSceneLinearByteEncoded4b() = default; + + constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba) + : ColorRGBA(rgba) { - return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - friend bool operator!=(const Color4b &a, const Color4b &b) + constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + : ColorRGBA(r, g, b, a) { - return !(a == b); } - uint64_t hash() const + /** + * Convert to back to float color. + */ + ColorSceneLinear4f decode() const + { + ColorSceneLinear4f decoded; + srgb_to_linearrgb_uchar4(decoded, *this); + return decoded; + } +}; + +/** + * Theme color template class. + * + * Don't use directly, but use `ColorTheme4b/ColorTheme4b`. + * + * This has been implemented as a template to improve inlining. When implemented as concrete + * classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be + * inlined. + */ +template +class ColorTheme4 final : public ColorRGBA { + public: + constexpr ColorTheme4() : ColorRGBA(){}; + + constexpr ColorTheme4(const ChannelStorageType *rgba) + : ColorRGBA(rgba) + { + } + + constexpr ColorTheme4(ChannelStorageType r, + ChannelStorageType g, + ChannelStorageType b, + ChannelStorageType a) + : ColorRGBA(r, g, b, a) + { + } + + /** + * Change precision of color to float. + */ + ColorTheme4 to_4f() const { - return static_cast(r * 1283591) ^ static_cast(g * 850177) ^ - static_cast(b * 735391) ^ static_cast(a * 442319); + if constexpr ((std::is_same_v)) { + return BLI_color_convert_to_theme4f(*this); + } + else { + return *this; + } + } + + /** + * Change precision of color to uint8_t. + */ + ColorTheme4 to_4b() const + { + if constexpr ((std::is_same_v)) { + return BLI_color_convert_to_theme4b(*this); + } + else { + return *this; + } } }; +using ColorTheme4b = ColorTheme4; +using ColorTheme4f = ColorTheme4; + +BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorTheme4f &theme4f) +{ + ColorTheme4b theme4b; + rgba_float_to_uchar(theme4b, theme4f); + return theme4b; +} + +BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorTheme4b &theme4b) +{ + ColorTheme4f theme4f; + rgba_uchar_to_float(theme4f, theme4b); + return theme4f; +} + +BLI_INLINE ColorSceneLinear4f BLI_color_convert_to_scene_linear( + const ColorTheme4f &theme4f) +{ + ColorSceneLinear4f scene_linear; + srgb_to_linearrgb_v4(scene_linear, theme4f); + return scene_linear; +} + +BLI_INLINE ColorSceneLinear4f BLI_color_convert_to_scene_linear( + const ColorTheme4b &theme4b) +{ + ColorSceneLinear4f scene_linear; + srgb_to_linearrgb_uchar4(scene_linear, theme4b); + return scene_linear; +} + +BLI_INLINE ColorTheme4f +BLI_color_convert_to_theme4f(const ColorSceneLinear4f &scene_linear) +{ + ColorTheme4f theme4f; + linearrgb_to_srgb_v4(theme4f, scene_linear); + return theme4f; +} + +BLI_INLINE ColorTheme4b +BLI_color_convert_to_theme4b(const ColorSceneLinear4f &scene_linear) +{ + ColorTheme4b theme4b; + linearrgb_to_srgb_uchar4(theme4b, scene_linear); + return theme4b; +} + +/* Internal roles. For convenience to shorten the type names and hide complexity. */ +using ColorGeometry4f = ColorSceneLinear4f; +using ColorGeometry4b = ColorSceneLinearByteEncoded4b; + } // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index ce3515ac153..f3dc343ee20 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC intern/BLI_args.c intern/BLI_array.c intern/BLI_assert.c + intern/BLI_color.cc intern/BLI_dial_2d.c intern/BLI_dynstr.c intern/BLI_filelist.c @@ -389,6 +390,7 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_color_test.cc tests/BLI_delaunay_2d_test.cc tests/BLI_disjoint_set_test.cc tests/BLI_edgehash_test.cc diff --git a/source/blender/blenlib/intern/BLI_color.cc b/source/blender/blenlib/intern/BLI_color.cc new file mode 100644 index 00000000000..6dcef4f4688 --- /dev/null +++ b/source/blender/blenlib/intern/BLI_color.cc @@ -0,0 +1,55 @@ +/* + * 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_color.hh" + +namespace blender { + +std::ostream &operator<<(std::ostream &stream, const eAlpha &space) +{ + switch (space) { + case eAlpha::Straight: { + stream << "Straight"; + break; + } + case eAlpha::Premultiplied: { + stream << "Premultiplied"; + break; + } + } + return stream; +} + +std::ostream &operator<<(std::ostream &stream, const eSpace &space) +{ + switch (space) { + case eSpace::Theme: { + stream << "Theme"; + break; + } + case eSpace::SceneLinear: { + stream << "SceneLinear"; + break; + } + case eSpace::SceneLinearByteEncoded: { + stream << "SceneLinearByteEncoded"; + break; + } + } + return stream; +} + +} // namespace blender diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc new file mode 100644 index 00000000000..14796e6bf71 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_color_test.cc @@ -0,0 +1,133 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_color.hh" + +namespace blender::tests { + +/** + * \name Conversions + * \{ */ + +TEST(color, ThemeByteToFloat) +{ + ColorTheme4b theme_byte(192, 128, 64, 128); + ColorTheme4f theme_float = theme_byte.to_4f(); + EXPECT_NEAR(0.75f, theme_float.r, 0.01f); + EXPECT_NEAR(0.5f, theme_float.g, 0.01f); + EXPECT_NEAR(0.25f, theme_float.b, 0.01f); + EXPECT_NEAR(0.5f, theme_float.a, 0.01f); +} + +TEST(color, SrgbStraightFloatToByte) +{ + ColorTheme4f theme_float(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme_byte = theme_float.to_4b(); + EXPECT_EQ(191, theme_byte.r); + EXPECT_EQ(128, theme_byte.g); + EXPECT_EQ(64, theme_byte.b); + EXPECT_EQ(128, theme_byte.a); +} + +TEST(color, SrgbStraightToSceneLinearPremultiplied) +{ + BLI_init_srgb_conversion(); + + ColorTheme4b theme(192, 128, 64, 128); + ColorSceneLinear4f linear = + BLI_color_convert_to_scene_linear(theme).premultiply_alpha(); + EXPECT_NEAR(0.26f, linear.r, 0.01f); + EXPECT_NEAR(0.11f, linear.g, 0.01f); + EXPECT_NEAR(0.02f, linear.b, 0.01f); + EXPECT_NEAR(0.5f, linear.a, 0.01f); +} + +TEST(color, SceneLinearStraightToPremultiplied) +{ + ColorSceneLinear4f straight(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinear4f premultiplied = straight.premultiply_alpha(); + EXPECT_NEAR(0.37f, premultiplied.r, 0.01f); + EXPECT_NEAR(0.25f, premultiplied.g, 0.01f); + EXPECT_NEAR(0.12f, premultiplied.b, 0.01f); + EXPECT_NEAR(0.5f, premultiplied.a, 0.01f); +} + +TEST(color, SceneLinearPremultipliedToStraight) +{ + ColorSceneLinear4f premultiplied(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinear4f straight = premultiplied.unpremultiply_alpha(); + EXPECT_NEAR(1.5f, straight.r, 0.01f); + EXPECT_NEAR(1.0f, straight.g, 0.01f); + EXPECT_NEAR(0.5f, straight.b, 0.01f); + EXPECT_NEAR(0.5f, straight.a, 0.01f); +} + +TEST(color, SceneLinearStraightSrgbFloat) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4f theme = BLI_color_convert_to_theme4f(linear); + EXPECT_NEAR(0.88f, theme.r, 0.01); + EXPECT_NEAR(0.73f, theme.g, 0.01); + EXPECT_NEAR(0.53f, theme.b, 0.01); + EXPECT_NEAR(0.5f, theme.a, 0.01); +} + +TEST(color, SceneLinearPremultipliedToSrgbFloat) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4f theme = BLI_color_convert_to_theme4f(linear.unpremultiply_alpha()); + + EXPECT_NEAR(1.19f, theme.r, 0.01); + EXPECT_NEAR(1.0f, theme.g, 0.01); + EXPECT_NEAR(0.74f, theme.b, 0.01); + EXPECT_NEAR(0.5f, theme.a, 0.01); +} + +TEST(color, SceneLinearStraightSrgbByte) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme = BLI_color_convert_to_theme4b(linear); + EXPECT_EQ(225, theme.r); + EXPECT_EQ(188, theme.g); + EXPECT_EQ(137, theme.b); + EXPECT_EQ(128, theme.a); +} + +TEST(color, SceneLinearPremultipliedToSrgbByte) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme = BLI_color_convert_to_theme4b(linear.unpremultiply_alpha()); + EXPECT_EQ(255, theme.r); + EXPECT_EQ(255, theme.g); + EXPECT_EQ(188, theme.b); + EXPECT_EQ(128, theme.a); +} + +TEST(color, SceneLinearByteEncoding) +{ + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinearByteEncoded4b encoded = linear.encode(); + EXPECT_EQ(225, encoded.r); + EXPECT_EQ(188, encoded.g); + EXPECT_EQ(137, encoded.b); + EXPECT_EQ(128, encoded.a); +} + +TEST(color, SceneLinearByteDecoding) +{ + ColorSceneLinearByteEncoded4b encoded(225, 188, 137, 128); + ColorSceneLinear4f decoded = encoded.decode(); + EXPECT_NEAR(0.75f, decoded.r, 0.01f); + EXPECT_NEAR(0.5f, decoded.g, 0.01f); + EXPECT_NEAR(0.25f, decoded.b, 0.01f); + EXPECT_NEAR(0.5f, decoded.a, 0.01f); +} + +/* \} */ + +} // namespace blender::tests diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 2c2ab9eaadd..5743f39f7da 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -266,7 +266,6 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_inner_points(struct Object * struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit); - #ifdef __cplusplus } #endif diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index d1e80f1d87e..c9b73aabf96 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -50,7 +50,7 @@ class CellValue { std::optional value_bool; std::optional value_float2; std::optional value_float3; - std::optional value_color; + std::optional value_color; std::optional value_object; std::optional value_collection; }; 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 02ffa1259fc..452885959f6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -116,7 +116,7 @@ std::unique_ptr GeometryDataSource::get_column_values( column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { - Color4f value; + ColorGeometry4f value; varray->get(index, &value); r_cell_value.value_color = value; }, diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index f1ca65817f6..8079763a339 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -170,7 +170,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { this->draw_float_vector(params, Span(&value.x, 3)); } else if (cell_value.value_color.has_value()) { - const Color4f value = *cell_value.value_color; + const ColorGeometry4f value = *cell_value.value_color; this->draw_float_vector(params, Span(&value.r, 4)); } else if (cell_value.value_object.has_value()) { diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index 53c5def57e9..9c2c1621e23 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -34,8 +34,8 @@ MAKE_CPP_TYPE(int32, int32_t) MAKE_CPP_TYPE(uint32, uint32_t) MAKE_CPP_TYPE(uint8, uint8_t) -MAKE_CPP_TYPE(Color4f, blender::Color4f) -MAKE_CPP_TYPE(Color4b, blender::Color4b) +MAKE_CPP_TYPE(ColorGeometry4f, blender::ColorGeometry4f) +MAKE_CPP_TYPE(ColorGeometry4b, blender::ColorGeometry4b) MAKE_CPP_TYPE(string, std::string) 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 21538db5455..71643df1cb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc @@ -101,9 +101,12 @@ template<> inline float3 clamp_value(const float3 val, const float3 min, const f return tmp; } -template<> inline Color4f clamp_value(const Color4f val, const Color4f min, const Color4f max) +template<> +inline ColorGeometry4f clamp_value(const ColorGeometry4f val, + const ColorGeometry4f min, + const ColorGeometry4f max) { - Color4f tmp; + ColorGeometry4f tmp; tmp.r = std::min(std::max(val.r, min.r), max.r); tmp.g = std::min(std::max(val.g, min.g), max.g); tmp.b = std::min(std::max(val.b, min.b), max.b); @@ -214,8 +217,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam break; } case CD_PROP_COLOR: { - Color4f min = params.get_input("Min_003"); - Color4f max = params.get_input("Max_003"); + ColorGeometry4f min = params.get_input("Min_003"); + ColorGeometry4f max = params.get_input("Max_003"); if (operation == NODE_CLAMP_RANGE) { if (min.r > max.r) { std::swap(min.r, max.r); @@ -230,8 +233,9 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam std::swap(min.a, max.a); } } - MutableSpan results = attribute_result.as_span(); - clamp_attribute(attribute_input->typed(), results, min, max); + MutableSpan results = attribute_result.as_span(); + clamp_attribute( + attribute_input->typed(), results, min, max); break; } default: { 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 b13e82e676d..5293dd8c876 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 @@ -83,8 +83,8 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon * currently. */ const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - OutputAttribute_Typed attribute_result = - component.attribute_try_get_for_output_only(result_name, result_domain); + OutputAttribute_Typed attribute_result = + component.attribute_try_get_for_output_only(result_name, result_domain); if (!attribute_result) { return; } @@ -92,7 +92,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon GVArray_Typed attribute_in = component.attribute_get_for_read( input_name, result_domain, 0.0f); - MutableSpan results = attribute_result.as_span(); + MutableSpan results = attribute_result.as_span(); ColorBand *color_ramp = &node_storage->color_ramp; parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { 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 a2ff1668a06..57ac68b4cd4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -131,16 +131,16 @@ static void do_equal_operation_float3(const VArray &input_a, } } -static void do_equal_operation_color4f(const VArray &input_a, - const VArray &input_b, +static void do_equal_operation_color4f(const VArray &input_a, + const VArray &input_b, const float threshold, MutableSpan span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const Color4f a = input_a[i]; - const Color4f b = input_b[i]; + const ColorGeometry4f a = input_a[i]; + const ColorGeometry4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) < threshold_squared; } } @@ -185,16 +185,16 @@ static void do_not_equal_operation_float3(const VArray &input_a, } } -static void do_not_equal_operation_color4f(const VArray &input_a, - const VArray &input_b, +static void do_not_equal_operation_color4f(const VArray &input_a, + const VArray &input_b, const float threshold, MutableSpan span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const Color4f a = input_a[i]; - const Color4f b = input_b[i]; + const ColorGeometry4f a = input_a[i]; + const ColorGeometry4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) >= threshold_squared; } } @@ -287,8 +287,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed(), attribute_b->typed(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_equal_operation_color4f( - attribute_a->typed(), attribute_b->typed(), threshold, result_span); + do_equal_operation_color4f(attribute_a->typed(), + attribute_b->typed(), + threshold, + result_span); } else if (input_data_type == CD_PROP_BOOL) { do_equal_operation_bool( @@ -305,8 +307,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed(), attribute_b->typed(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_not_equal_operation_color4f( - attribute_a->typed(), attribute_b->typed(), threshold, result_span); + do_not_equal_operation_color4f(attribute_a->typed(), + attribute_b->typed(), + threshold, + result_span); } else if (input_data_type == CD_PROP_BOOL) { do_not_equal_operation_bool( 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 2fc86269797..599c9e58e52 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 @@ -165,9 +165,10 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon } case CD_PROP_COLOR: { const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb; - GVArray_Typed attribute_in = component.attribute_get_for_read( - input_name, result_domain, Color4f(0.0f, 0.0f, 0.0f, 1.0f)); - MutableSpan results = attribute_result.as_span(); + GVArray_Typed attribute_in = + component.attribute_get_for_read( + input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + MutableSpan results = attribute_result.as_span(); parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { for (const int i : range) { BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); 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 60522fd0f72..389abe3b2aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -110,7 +110,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams break; } case CD_PROP_COLOR: { - const Color4f value = params.get_input("Value_002"); + const ColorGeometry4f value = params.get_input("Value_002"); attribute->fill(&value); break; } 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 e502a183ef5..a6bd6c0ee32 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -120,16 +120,16 @@ static void do_mix_operation_float3(const int blend_mode, static void do_mix_operation_color4f(const int blend_mode, const VArray &factors, - const VArray &inputs_a, - const VArray &inputs_b, - VMutableArray &results) + const VArray &inputs_a, + const VArray &inputs_b, + VMutableArray &results) { const int size = results.size(); parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float factor = factors[i]; - Color4f a = inputs_a[i]; - const Color4f b = inputs_b[i]; + ColorGeometry4f a = inputs_a[i]; + const ColorGeometry4f b = inputs_b[i]; ramp_blend(blend_mode, a, factor, b); results.set(i, a); } @@ -160,9 +160,9 @@ static void do_mix_operation(const CustomDataType result_type, else if (result_type == CD_PROP_COLOR) { do_mix_operation_color4f(blend_mode, attribute_factor, - attribute_a.typed(), - attribute_b.typed(), - attribute_result.typed()); + attribute_a.typed(), + attribute_b.typed(), + attribute_result.typed()); } } 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 aa558314b9e..d6b1ad3e9e0 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 @@ -79,8 +79,9 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec const AttributeDomain result_domain = get_result_domain( component, result_attribute_name, mapping_name); - OutputAttribute_Typed attribute_out = - component.attribute_try_get_for_output_only(result_attribute_name, result_domain); + OutputAttribute_Typed attribute_out = + component.attribute_try_get_for_output_only(result_attribute_name, + result_domain); if (!attribute_out) { return; } @@ -88,7 +89,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec GVArray_Typed mapping_attribute = component.attribute_get_for_read( mapping_name, result_domain, {0, 0, 0}); - MutableSpan colors = attribute_out.as_span(); + MutableSpan colors = attribute_out.as_span(); parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { for (const int i : range) { TexResult texture_result = {0}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index e0daae0c172..049ba5d3143 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -134,7 +134,7 @@ static void geo_node_switch_exec(GeoNodeExecParams params) break; } case SOCK_RGBA: { - output_input(params, input, "_004", "Output_004"); + output_input(params, input, "_004", "Output_004"); break; } case SOCK_STRING: { diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 73a702c753a..188d198e159 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -104,9 +104,10 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, return std::make_unique(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_RGBA) { - const Color4f value = this->get_input(found_socket->identifier); + const ColorGeometry4f value = this->get_input(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); - conversions.convert_to_uninitialized(CPPType::get(), *cpp_type, &value, buffer); + conversions.convert_to_uninitialized( + CPPType::get(), *cpp_type, &value, buffer); return std::make_unique(*cpp_type, domain_size, buffer); } BLI_assert(false); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index ce2848b52a0..783a7a9b3d8 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -637,9 +637,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; + *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; return socktype; } diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc index 63f7b8a9ee8..220e5ea9046 100644 --- a/source/blender/nodes/intern/type_conversions.cc +++ b/source/blender/nodes/intern/type_conversions.cc @@ -66,9 +66,9 @@ static bool float_to_bool(const float &a) { return a > 0.0f; } -static Color4f float_to_color(const float &a) +static ColorGeometry4f float_to_color(const float &a) { - return Color4f(a, a, a, 1.0f); + return ColorGeometry4f(a, a, a, 1.0f); } static float3 float2_to_float3(const float2 &a) @@ -87,9 +87,9 @@ static bool float2_to_bool(const float2 &a) { return !is_zero_v2(a); } -static Color4f float2_to_color(const float2 &a) +static ColorGeometry4f float2_to_color(const float2 &a) { - return Color4f(a.x, a.y, 0.0f, 1.0f); + return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f); } static bool float3_to_bool(const float3 &a) @@ -108,9 +108,9 @@ static float2 float3_to_float2(const float3 &a) { return float2(a); } -static Color4f float3_to_color(const float3 &a) +static ColorGeometry4f float3_to_color(const float3 &a) { - return Color4f(a.x, a.y, a.z, 1.0f); + return ColorGeometry4f(a.x, a.y, a.z, 1.0f); } static bool int_to_bool(const int32_t &a) @@ -129,9 +129,9 @@ static float3 int_to_float3(const int32_t &a) { return float3((float)a); } -static Color4f int_to_color(const int32_t &a) +static ColorGeometry4f int_to_color(const int32_t &a) { - return Color4f((float)a, (float)a, (float)a, 1.0f); + return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); } static float bool_to_float(const bool &a) @@ -150,28 +150,28 @@ static float3 bool_to_float3(const bool &a) { return (a) ? float3(1.0f) : float3(0.0f); } -static Color4f bool_to_color(const bool &a) +static ColorGeometry4f bool_to_color(const bool &a) { - return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f); + return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f); } -static bool color_to_bool(const Color4f &a) +static bool color_to_bool(const ColorGeometry4f &a) { return rgb_to_grayscale(a) > 0.0f; } -static float color_to_float(const Color4f &a) +static float color_to_float(const ColorGeometry4f &a) { return rgb_to_grayscale(a); } -static int32_t color_to_int(const Color4f &a) +static int32_t color_to_int(const ColorGeometry4f &a) { return (int)rgb_to_grayscale(a); } -static float2 color_to_float2(const Color4f &a) +static float2 color_to_float2(const ColorGeometry4f &a) { return float2(a.r, a.g); } -static float3 color_to_float3(const Color4f &a) +static float3 color_to_float3(const ColorGeometry4f &a) { return float3(a.r, a.g, a.b); } @@ -184,37 +184,37 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); return conversions; } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index 8ca4a6bab5f..a7239154633 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -70,7 +70,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { static blender::fn::MFSignature create_signature() { blender::fn::MFSignatureBuilder signature{"Separate RGB"}; - signature.single_input("Color"); + signature.single_input("Color"); signature.single_output("R"); signature.single_output("G"); signature.single_output("B"); @@ -81,14 +81,14 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext UNUSED(context)) const override { - const blender::VArray &colors = - params.readonly_single_input(0, "Color"); + const blender::VArray &colors = + params.readonly_single_input(0, "Color"); blender::MutableSpan rs = params.uninitialized_single_output(1, "R"); blender::MutableSpan gs = params.uninitialized_single_output(2, "G"); blender::MutableSpan bs = params.uninitialized_single_output(3, "B"); for (int64_t i : mask) { - blender::Color4f color = colors[i]; + blender::ColorGeometry4f color = colors[i]; rs[i] = color.r; gs[i] = color.g; bs[i] = color.b; @@ -155,8 +155,9 @@ static int gpu_shader_combrgb(GPUMaterial *mat, static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO fn{ - "Combine RGB", [](float r, float g, float b) { return blender::Color4f(r, g, b, 1.0f); }}; + static blender::fn::CustomMF_SI_SI_SI_SO fn{ + "Combine RGB", + [](float r, float g, float b) { return blender::ColorGeometry4f(r, g, b, 1.0f); }}; builder.set_matching_fn(fn); } diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 90e8161c09f..5b2eb300aac 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -140,7 +140,7 @@ class ColorBandFunction : public blender::fn::MultiFunction { { blender::fn::MFSignatureBuilder signature{"Color Band"}; signature.single_input("Value"); - signature.single_output("Color"); + signature.single_output("Color"); signature.single_output("Alpha"); return signature.build(); } @@ -150,12 +150,12 @@ class ColorBandFunction : public blender::fn::MultiFunction { blender::fn::MFContext UNUSED(context)) const override { const blender::VArray &values = params.readonly_single_input(0, "Value"); - blender::MutableSpan colors = - params.uninitialized_single_output(1, "Color"); + blender::MutableSpan colors = + params.uninitialized_single_output(1, "Color"); blender::MutableSpan alphas = params.uninitialized_single_output(2, "Alpha"); for (int64_t i : mask) { - blender::Color4f color; + blender::ColorGeometry4f color; BKE_colorband_evaluate(&color_band_, values[i], color); colors[i] = color; alphas[i] = color.a; -- cgit v1.2.3 From 00955cd31eda95aca619842064d22663af4c812b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 25 May 2021 17:03:54 +0200 Subject: Revert "Blenlib: Explicit Colors." This reverts commit fd94e033446c72fb92048a9864c1d539fccde59a. does not compile against latest master. --- source/blender/blenkernel/BKE_attribute_math.hh | 35 +-- .../blender/blenkernel/intern/attribute_access.cc | 8 +- source/blender/blenkernel/intern/attribute_math.cc | 15 +- .../blenkernel/intern/geometry_component_mesh.cc | 20 +- source/blender/blenlib/BLI_color.hh | 302 +++------------------ source/blender/blenlib/CMakeLists.txt | 2 - source/blender/blenlib/intern/BLI_color.cc | 55 ---- source/blender/blenlib/tests/BLI_color_test.cc | 133 --------- source/blender/draw/intern/draw_cache_impl.h | 1 + .../space_spreadsheet/spreadsheet_cell_value.hh | 2 +- .../spreadsheet_data_source_geometry.cc | 2 +- .../space_spreadsheet/spreadsheet_layout.cc | 2 +- source/blender/functions/intern/cpp_types.cc | 4 +- .../geometry/nodes/node_geo_attribute_clamp.cc | 16 +- .../nodes/node_geo_attribute_color_ramp.cc | 6 +- .../geometry/nodes/node_geo_attribute_compare.cc | 28 +- .../geometry/nodes/node_geo_attribute_curve_map.cc | 7 +- .../geometry/nodes/node_geo_attribute_fill.cc | 2 +- .../nodes/geometry/nodes/node_geo_attribute_mix.cc | 16 +- .../nodes/node_geo_attribute_sample_texture.cc | 7 +- .../nodes/geometry/nodes/node_geo_switch.cc | 2 +- source/blender/nodes/intern/node_geometry_exec.cc | 5 +- source/blender/nodes/intern/node_socket.cc | 4 +- source/blender/nodes/intern/type_conversions.cc | 50 ++-- .../nodes/shader/nodes/node_shader_sepcombRGB.cc | 13 +- .../nodes/shader/nodes/node_shader_valToRgb.cc | 8 +- 26 files changed, 151 insertions(+), 594 deletions(-) delete mode 100644 source/blender/blenlib/intern/BLI_color.cc delete mode 100644 source/blender/blenlib/tests/BLI_color_test.cc diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index ba683362e69..0afdc436415 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -52,7 +52,7 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f func(bool()); break; case CD_PROP_COLOR: - func(ColorGeometry4f()); + func(Color4f()); break; default: BLI_assert_unreachable(); @@ -78,8 +78,8 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func else if (cpp_type.is()) { func(bool()); } - else if (cpp_type.is()) { - func(ColorGeometry4f()); + else if (cpp_type.is()) { + func(Color4f()); } else { BLI_assert_unreachable(); @@ -123,12 +123,9 @@ inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, co } template<> -inline ColorGeometry4f mix3(const float3 &weights, - const ColorGeometry4f &v0, - const ColorGeometry4f &v1, - const ColorGeometry4f &v2) +inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1, const Color4f &v2) { - ColorGeometry4f result; + Color4f result; interp_v4_v4v4v4(result, v0, v1, v2, weights); return result; } @@ -168,10 +165,9 @@ template<> inline float3 mix2(const float factor, const float3 &a, const float3 return float3::interpolate(a, b, factor); } -template<> -inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b) +template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b) { - ColorGeometry4f result; + Color4f result; interp_v4_v4v4(result, a, b, factor); return result; } @@ -278,16 +274,15 @@ class SimpleMixerWithAccumulationType { } }; -class ColorGeometryMixer { +class Color4fMixer { private: - MutableSpan buffer_; - ColorGeometry4f default_color_; + MutableSpan buffer_; + Color4f default_color_; Array total_weights_; public: - ColorGeometryMixer(MutableSpan buffer, - ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); - void mix_in(const int64_t index, const ColorGeometry4f &color, const float weight = 1.0f); + Color4fMixer(MutableSpan buffer, Color4f default_color = {0, 0, 0, 1}); + void mix_in(const int64_t index, const Color4f &color, const float weight = 1.0f); void finalize(); }; @@ -304,10 +299,10 @@ template<> struct DefaultMixerStruct { template<> struct DefaultMixerStruct { using type = SimpleMixer; }; -template<> struct DefaultMixerStruct { - /* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not +template<> struct DefaultMixerStruct { + /* Use a special mixer for colors. Color4f can't be added/multiplied, because this is not * something one should usually do with colors. */ - using type = ColorGeometryMixer; + using type = Color4fMixer; }; template<> struct DefaultMixerStruct { static int double_to_int(const double &value) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d36e9ed3e86..62833e10438 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -61,7 +61,7 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty case CD_PROP_INT32: return &CPPType::get(); case CD_PROP_COLOR: - return &CPPType::get(); + return &CPPType::get(); case CD_PROP_BOOL: return &CPPType::get(); default: @@ -84,7 +84,7 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) if (type.is()) { return CD_PROP_INT32; } - if (type.is()) { + if (type.is()) { return CD_PROP_COLOR; } if (type.is()) { @@ -355,7 +355,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( case CD_PROP_INT32: return this->layer_to_read_attribute(layer, domain_size); case CD_PROP_COLOR: - return this->layer_to_read_attribute(layer, domain_size); + return this->layer_to_read_attribute(layer, domain_size); case CD_PROP_BOOL: return this->layer_to_read_attribute(layer, domain_size); default: @@ -389,7 +389,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( case CD_PROP_INT32: return this->layer_to_write_attribute(layer, domain_size); case CD_PROP_COLOR: - return this->layer_to_write_attribute(layer, domain_size); + return this->layer_to_write_attribute(layer, domain_size); case CD_PROP_BOOL: return this->layer_to_write_attribute(layer, domain_size); default: diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc index 5cdf329effb..4ff3a6ceff5 100644 --- a/source/blender/blenkernel/intern/attribute_math.cc +++ b/source/blender/blenkernel/intern/attribute_math.cc @@ -18,21 +18,18 @@ namespace blender::attribute_math { -ColorGeometryMixer::ColorGeometryMixer(MutableSpan output_buffer, - ColorGeometry4f default_color) +Color4fMixer::Color4fMixer(MutableSpan output_buffer, Color4f default_color) : buffer_(output_buffer), default_color_(default_color), total_weights_(output_buffer.size(), 0.0f) { - buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); + buffer_.fill(Color4f(0, 0, 0, 0)); } -void ColorGeometryMixer::mix_in(const int64_t index, - const ColorGeometry4f &color, - const float weight) +void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float weight) { BLI_assert(weight >= 0.0f); - ColorGeometry4f &output_color = buffer_[index]; + Color4f &output_color = buffer_[index]; output_color.r += color.r * weight; output_color.g += color.g * weight; output_color.b += color.b * weight; @@ -40,11 +37,11 @@ void ColorGeometryMixer::mix_in(const int64_t index, total_weights_[index] += weight; } -void ColorGeometryMixer::finalize() +void Color4fMixer::finalize() { for (const int64_t i : buffer_.index_range()) { const float weight = total_weights_[i]; - ColorGeometry4f &output_color = buffer_[i]; + Color4f &output_color = buffer_[i]; if (weight > 0.0f) { const float weight_inv = 1.0f / weight; output_color.r *= weight_inv; diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 42f3a854aec..9e622ab2cdf 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -773,20 +773,18 @@ static void set_loop_uv(MLoopUV &uv, float2 co) copy_v2_v2(uv.uv, co); } -static ColorGeometry4f get_loop_color(const MLoopCol &col) +static Color4f get_loop_color(const MLoopCol &col) { - ColorGeometry4b encoded_color = ColorGeometry4b(col.r, col.g, col.b, col.a); - ColorGeometry4f linear_color = encoded_color.decode(); + Color4f srgb_color; + rgba_uchar_to_float(srgb_color, &col.r); + Color4f linear_color; + srgb_to_linearrgb_v4(linear_color, srgb_color); return linear_color; } -static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color) +static void set_loop_color(MLoopCol &col, Color4f linear_color) { - ColorGeometry4b encoded_color = linear_color.encode(); - col.r = encoded_color.r; - col.g = encoded_color.g; - col.b = encoded_color.b; - col.a = encoded_color.a; + linearrgb_to_srgb_uchar4(&col.r, linear_color); } static float get_crease(const MEdge &edge) @@ -1123,8 +1121,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_COLOR, CD_MLOOPCOL, corner_access, - make_derived_read_attribute, - make_derived_write_attribute); + make_derived_read_attribute, + make_derived_write_attribute); static VertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index 287587e04be..e57a5109a66 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -22,122 +22,41 @@ namespace blender { -/** - * CPP based color structures. - * - * Strongly typed color storage structures with space and alpha association. - * Will increase readability and visibility of typical mistakes when - * working with colors. - * - * The storage structs can hold 4 channels (r, g, b and a). - * - * Usage: - * - * Convert a theme byte color to a linearrgb premultiplied. - * ``` - * ColorTheme4b theme_color; - * ColorSceneLinear4f linearrgb_color = - * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha(); - * ``` - * - * The API is structured to make most use of inlining. Most notable are space - * conversions done via `BLI_color_convert_to*` functions. - * - * - Conversions between spaces (theme <=> scene linear) should always be done by - * invoking the `BLI_color_convert_to*` methods. - * - Encoding colors (compressing to store colors inside a less precision storage) - * should be done by invoking the `encode` and `decode` methods. - * - Changing alpha association should be done by invoking `premultiply_alpha` or - * `unpremultiply_alpha` methods. - * - * # Encoding. - * - * Color encoding is used to store colors with less precision as in using `uint8_t` in - * stead of `float`. This encoding is supported for `eSpace::SceneLinear`. - * To make this clear to the developer the `eSpace::SceneLinearByteEncoded` - * space is added. - * - * # Precision - * - * Colors can be stored using `uint8_t` or `float` colors. The conversion - * between the two precisions are available as methods. (`to_4b` and - * `to_4f`). - * - * # Alpha conversion - * - * Alpha conversion is only supported in SceneLinear space. - * - * Extending this file: - * - This file can be extended with `ColorHex/Hsl/Hsv` for different representations - * of rgb based colors. `ColorHsl4f` - * - Add non RGB spaces/storages ColorXyz. - */ - -/* Enumeration containing the different alpha modes. */ -enum class eAlpha { - /* Color and alpha are unassociated. */ - Straight, - /* Color and alpha are associated. */ - Premultiplied, -}; -std::ostream &operator<<(std::ostream &stream, const eAlpha &space); +struct Color4f { + float r, g, b, a; -/* Enumeration containing internal spaces. */ -enum class eSpace { - /* Blender theme color space (sRGB). */ - Theme, - /* Blender internal scene linear color space (maps to SceneReference role in OCIO). */ - SceneLinear, - /* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */ - SceneLinearByteEncoded, -}; -std::ostream &operator<<(std::ostream &stream, const eSpace &space); + Color4f() = default; -/* Template class to store RGBA values with different precision, space and alpha association. */ -template class ColorRGBA { - public: - ChannelStorageType r, g, b, a; - constexpr ColorRGBA() = default; - - constexpr ColorRGBA(const ChannelStorageType rgba[4]) - : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) + Color4f(const float *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) { } - constexpr ColorRGBA(const ChannelStorageType r, - const ChannelStorageType g, - const ChannelStorageType b, - const ChannelStorageType a) - : r(r), g(g), b(b), a(a) + Color4f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) { } - operator ChannelStorageType *() + operator float *() { return &r; } - operator const ChannelStorageType *() const + operator const float *() const { return &r; } - friend std::ostream &operator<<(std::ostream &stream, - const ColorRGBA &c) + friend std::ostream &operator<<(std::ostream &stream, Color4f c) { - - stream << Space << Alpha << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; + stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; return stream; } - friend bool operator==(const ColorRGBA &a, - const ColorRGBA &b) + friend bool operator==(const Color4f &a, const Color4f &b) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - friend bool operator!=(const ColorRGBA &a, - const ColorRGBA &b) + friend bool operator!=(const Color4f &a, const Color4f &b) { return !(a == b); } @@ -152,209 +71,58 @@ template class ColorRGB } }; -/* Forward declarations of concrete color classes. */ -template class ColorSceneLinear4f; -template class ColorSceneLinearByteEncoded4b; -template class ColorTheme4; - -/* Forward declation of precision conversion methods. */ -BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4f(const ColorTheme4 &srgb4b); -BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4b(const ColorTheme4 &srgb4f); - -template -class ColorSceneLinear4f final : public ColorRGBA { - public: - constexpr ColorSceneLinear4f() : ColorRGBA() - { - } - - constexpr ColorSceneLinear4f(const float *rgba) - : ColorRGBA(rgba) - { - } - - constexpr ColorSceneLinear4f(float r, float g, float b, float a) - : ColorRGBA(r, g, b, a) - { - } +struct Color4b { + uint8_t r, g, b, a; - /** - * Convert to its byte encoded counter space. - **/ - ColorSceneLinearByteEncoded4b encode() const - { - ColorSceneLinearByteEncoded4b encoded; - linearrgb_to_srgb_uchar4(encoded, *this); - return encoded; - } + Color4b() = default; - /** - * Convert color and alpha association to premultiplied alpha. - * - * Does nothing when color has already a premultiplied alpha. - */ - ColorSceneLinear4f premultiply_alpha() const + Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) { - if constexpr (Alpha == eAlpha::Straight) { - ColorSceneLinear4f premultiplied; - straight_to_premul_v4_v4(premultiplied, *this); - return premultiplied; - } - else { - return *this; - } } - /** - * Convert color and alpha association to straight alpha. - * - * Does nothing when color has straighten alpha. - */ - ColorSceneLinear4f unpremultiply_alpha() const + Color4b(Color4f other) { - if constexpr (Alpha == eAlpha::Premultiplied) { - ColorSceneLinear4f straighten; - premul_to_straight_v4_v4(straighten, *this); - return straighten; - } - else { - return *this; - } + rgba_float_to_uchar(*this, other); } -}; - -template -class ColorSceneLinearByteEncoded4b final - : public ColorRGBA { - public: - constexpr ColorSceneLinearByteEncoded4b() = default; - constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba) - : ColorRGBA(rgba) + operator Color4f() const { + Color4f result; + rgba_uchar_to_float(result, *this); + return result; } - constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) - : ColorRGBA(r, g, b, a) + operator uint8_t *() { + return &r; } - /** - * Convert to back to float color. - */ - ColorSceneLinear4f decode() const + operator const uint8_t *() const { - ColorSceneLinear4f decoded; - srgb_to_linearrgb_uchar4(decoded, *this); - return decoded; + return &r; } -}; - -/** - * Theme color template class. - * - * Don't use directly, but use `ColorTheme4b/ColorTheme4b`. - * - * This has been implemented as a template to improve inlining. When implemented as concrete - * classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be - * inlined. - */ -template -class ColorTheme4 final : public ColorRGBA { - public: - constexpr ColorTheme4() : ColorRGBA(){}; - constexpr ColorTheme4(const ChannelStorageType *rgba) - : ColorRGBA(rgba) + friend std::ostream &operator<<(std::ostream &stream, Color4b c) { + stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; + return stream; } - constexpr ColorTheme4(ChannelStorageType r, - ChannelStorageType g, - ChannelStorageType b, - ChannelStorageType a) - : ColorRGBA(r, g, b, a) + friend bool operator==(const Color4b &a, const Color4b &b) { + return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - /** - * Change precision of color to float. - */ - ColorTheme4 to_4f() const + friend bool operator!=(const Color4b &a, const Color4b &b) { - if constexpr ((std::is_same_v)) { - return BLI_color_convert_to_theme4f(*this); - } - else { - return *this; - } + return !(a == b); } - /** - * Change precision of color to uint8_t. - */ - ColorTheme4 to_4b() const + uint64_t hash() const { - if constexpr ((std::is_same_v)) { - return BLI_color_convert_to_theme4b(*this); - } - else { - return *this; - } + return static_cast(r * 1283591) ^ static_cast(g * 850177) ^ + static_cast(b * 735391) ^ static_cast(a * 442319); } }; -using ColorTheme4b = ColorTheme4; -using ColorTheme4f = ColorTheme4; - -BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorTheme4f &theme4f) -{ - ColorTheme4b theme4b; - rgba_float_to_uchar(theme4b, theme4f); - return theme4b; -} - -BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorTheme4b &theme4b) -{ - ColorTheme4f theme4f; - rgba_uchar_to_float(theme4f, theme4b); - return theme4f; -} - -BLI_INLINE ColorSceneLinear4f BLI_color_convert_to_scene_linear( - const ColorTheme4f &theme4f) -{ - ColorSceneLinear4f scene_linear; - srgb_to_linearrgb_v4(scene_linear, theme4f); - return scene_linear; -} - -BLI_INLINE ColorSceneLinear4f BLI_color_convert_to_scene_linear( - const ColorTheme4b &theme4b) -{ - ColorSceneLinear4f scene_linear; - srgb_to_linearrgb_uchar4(scene_linear, theme4b); - return scene_linear; -} - -BLI_INLINE ColorTheme4f -BLI_color_convert_to_theme4f(const ColorSceneLinear4f &scene_linear) -{ - ColorTheme4f theme4f; - linearrgb_to_srgb_v4(theme4f, scene_linear); - return theme4f; -} - -BLI_INLINE ColorTheme4b -BLI_color_convert_to_theme4b(const ColorSceneLinear4f &scene_linear) -{ - ColorTheme4b theme4b; - linearrgb_to_srgb_uchar4(theme4b, scene_linear); - return theme4b; -} - -/* Internal roles. For convenience to shorten the type names and hide complexity. */ -using ColorGeometry4f = ColorSceneLinear4f; -using ColorGeometry4b = ColorSceneLinearByteEncoded4b; - } // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index f3dc343ee20..ce3515ac153 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -39,7 +39,6 @@ set(SRC intern/BLI_args.c intern/BLI_array.c intern/BLI_assert.c - intern/BLI_color.cc intern/BLI_dial_2d.c intern/BLI_dynstr.c intern/BLI_filelist.c @@ -390,7 +389,6 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc - tests/BLI_color_test.cc tests/BLI_delaunay_2d_test.cc tests/BLI_disjoint_set_test.cc tests/BLI_edgehash_test.cc diff --git a/source/blender/blenlib/intern/BLI_color.cc b/source/blender/blenlib/intern/BLI_color.cc deleted file mode 100644 index 6dcef4f4688..00000000000 --- a/source/blender/blenlib/intern/BLI_color.cc +++ /dev/null @@ -1,55 +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 "BLI_color.hh" - -namespace blender { - -std::ostream &operator<<(std::ostream &stream, const eAlpha &space) -{ - switch (space) { - case eAlpha::Straight: { - stream << "Straight"; - break; - } - case eAlpha::Premultiplied: { - stream << "Premultiplied"; - break; - } - } - return stream; -} - -std::ostream &operator<<(std::ostream &stream, const eSpace &space) -{ - switch (space) { - case eSpace::Theme: { - stream << "Theme"; - break; - } - case eSpace::SceneLinear: { - stream << "SceneLinear"; - break; - } - case eSpace::SceneLinearByteEncoded: { - stream << "SceneLinearByteEncoded"; - break; - } - } - return stream; -} - -} // namespace blender diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc deleted file mode 100644 index 14796e6bf71..00000000000 --- a/source/blender/blenlib/tests/BLI_color_test.cc +++ /dev/null @@ -1,133 +0,0 @@ -/* Apache License, Version 2.0 */ - -#include "testing/testing.h" - -#include "BLI_color.hh" - -namespace blender::tests { - -/** - * \name Conversions - * \{ */ - -TEST(color, ThemeByteToFloat) -{ - ColorTheme4b theme_byte(192, 128, 64, 128); - ColorTheme4f theme_float = theme_byte.to_4f(); - EXPECT_NEAR(0.75f, theme_float.r, 0.01f); - EXPECT_NEAR(0.5f, theme_float.g, 0.01f); - EXPECT_NEAR(0.25f, theme_float.b, 0.01f); - EXPECT_NEAR(0.5f, theme_float.a, 0.01f); -} - -TEST(color, SrgbStraightFloatToByte) -{ - ColorTheme4f theme_float(0.75f, 0.5f, 0.25f, 0.5f); - ColorTheme4b theme_byte = theme_float.to_4b(); - EXPECT_EQ(191, theme_byte.r); - EXPECT_EQ(128, theme_byte.g); - EXPECT_EQ(64, theme_byte.b); - EXPECT_EQ(128, theme_byte.a); -} - -TEST(color, SrgbStraightToSceneLinearPremultiplied) -{ - BLI_init_srgb_conversion(); - - ColorTheme4b theme(192, 128, 64, 128); - ColorSceneLinear4f linear = - BLI_color_convert_to_scene_linear(theme).premultiply_alpha(); - EXPECT_NEAR(0.26f, linear.r, 0.01f); - EXPECT_NEAR(0.11f, linear.g, 0.01f); - EXPECT_NEAR(0.02f, linear.b, 0.01f); - EXPECT_NEAR(0.5f, linear.a, 0.01f); -} - -TEST(color, SceneLinearStraightToPremultiplied) -{ - ColorSceneLinear4f straight(0.75f, 0.5f, 0.25f, 0.5f); - ColorSceneLinear4f premultiplied = straight.premultiply_alpha(); - EXPECT_NEAR(0.37f, premultiplied.r, 0.01f); - EXPECT_NEAR(0.25f, premultiplied.g, 0.01f); - EXPECT_NEAR(0.12f, premultiplied.b, 0.01f); - EXPECT_NEAR(0.5f, premultiplied.a, 0.01f); -} - -TEST(color, SceneLinearPremultipliedToStraight) -{ - ColorSceneLinear4f premultiplied(0.75f, 0.5f, 0.25f, 0.5f); - ColorSceneLinear4f straight = premultiplied.unpremultiply_alpha(); - EXPECT_NEAR(1.5f, straight.r, 0.01f); - EXPECT_NEAR(1.0f, straight.g, 0.01f); - EXPECT_NEAR(0.5f, straight.b, 0.01f); - EXPECT_NEAR(0.5f, straight.a, 0.01f); -} - -TEST(color, SceneLinearStraightSrgbFloat) -{ - BLI_init_srgb_conversion(); - ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); - ColorTheme4f theme = BLI_color_convert_to_theme4f(linear); - EXPECT_NEAR(0.88f, theme.r, 0.01); - EXPECT_NEAR(0.73f, theme.g, 0.01); - EXPECT_NEAR(0.53f, theme.b, 0.01); - EXPECT_NEAR(0.5f, theme.a, 0.01); -} - -TEST(color, SceneLinearPremultipliedToSrgbFloat) -{ - BLI_init_srgb_conversion(); - ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); - ColorTheme4f theme = BLI_color_convert_to_theme4f(linear.unpremultiply_alpha()); - - EXPECT_NEAR(1.19f, theme.r, 0.01); - EXPECT_NEAR(1.0f, theme.g, 0.01); - EXPECT_NEAR(0.74f, theme.b, 0.01); - EXPECT_NEAR(0.5f, theme.a, 0.01); -} - -TEST(color, SceneLinearStraightSrgbByte) -{ - BLI_init_srgb_conversion(); - ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); - ColorTheme4b theme = BLI_color_convert_to_theme4b(linear); - EXPECT_EQ(225, theme.r); - EXPECT_EQ(188, theme.g); - EXPECT_EQ(137, theme.b); - EXPECT_EQ(128, theme.a); -} - -TEST(color, SceneLinearPremultipliedToSrgbByte) -{ - BLI_init_srgb_conversion(); - ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); - ColorTheme4b theme = BLI_color_convert_to_theme4b(linear.unpremultiply_alpha()); - EXPECT_EQ(255, theme.r); - EXPECT_EQ(255, theme.g); - EXPECT_EQ(188, theme.b); - EXPECT_EQ(128, theme.a); -} - -TEST(color, SceneLinearByteEncoding) -{ - ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); - ColorSceneLinearByteEncoded4b encoded = linear.encode(); - EXPECT_EQ(225, encoded.r); - EXPECT_EQ(188, encoded.g); - EXPECT_EQ(137, encoded.b); - EXPECT_EQ(128, encoded.a); -} - -TEST(color, SceneLinearByteDecoding) -{ - ColorSceneLinearByteEncoded4b encoded(225, 188, 137, 128); - ColorSceneLinear4f decoded = encoded.decode(); - EXPECT_NEAR(0.75f, decoded.r, 0.01f); - EXPECT_NEAR(0.5f, decoded.g, 0.01f); - EXPECT_NEAR(0.25f, decoded.b, 0.01f); - EXPECT_NEAR(0.5f, decoded.a, 0.01f); -} - -/* \} */ - -} // namespace blender::tests diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 5743f39f7da..2c2ab9eaadd 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -266,6 +266,7 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_inner_points(struct Object * struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index c9b73aabf96..d1e80f1d87e 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -50,7 +50,7 @@ class CellValue { std::optional value_bool; std::optional value_float2; std::optional value_float3; - std::optional value_color; + std::optional value_color; std::optional value_object; std::optional value_collection; }; 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 452885959f6..02ffa1259fc 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -116,7 +116,7 @@ std::unique_ptr GeometryDataSource::get_column_values( column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { - ColorGeometry4f value; + Color4f value; varray->get(index, &value); r_cell_value.value_color = value; }, diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index 8079763a339..f1ca65817f6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -170,7 +170,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { this->draw_float_vector(params, Span(&value.x, 3)); } else if (cell_value.value_color.has_value()) { - const ColorGeometry4f value = *cell_value.value_color; + const Color4f value = *cell_value.value_color; this->draw_float_vector(params, Span(&value.r, 4)); } else if (cell_value.value_object.has_value()) { diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index 9c2c1621e23..53c5def57e9 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -34,8 +34,8 @@ MAKE_CPP_TYPE(int32, int32_t) MAKE_CPP_TYPE(uint32, uint32_t) MAKE_CPP_TYPE(uint8, uint8_t) -MAKE_CPP_TYPE(ColorGeometry4f, blender::ColorGeometry4f) -MAKE_CPP_TYPE(ColorGeometry4b, blender::ColorGeometry4b) +MAKE_CPP_TYPE(Color4f, blender::Color4f) +MAKE_CPP_TYPE(Color4b, blender::Color4b) MAKE_CPP_TYPE(string, std::string) 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 71643df1cb6..21538db5455 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc @@ -101,12 +101,9 @@ template<> inline float3 clamp_value(const float3 val, const float3 min, const f return tmp; } -template<> -inline ColorGeometry4f clamp_value(const ColorGeometry4f val, - const ColorGeometry4f min, - const ColorGeometry4f max) +template<> inline Color4f clamp_value(const Color4f val, const Color4f min, const Color4f max) { - ColorGeometry4f tmp; + Color4f tmp; tmp.r = std::min(std::max(val.r, min.r), max.r); tmp.g = std::min(std::max(val.g, min.g), max.g); tmp.b = std::min(std::max(val.b, min.b), max.b); @@ -217,8 +214,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam break; } case CD_PROP_COLOR: { - ColorGeometry4f min = params.get_input("Min_003"); - ColorGeometry4f max = params.get_input("Max_003"); + Color4f min = params.get_input("Min_003"); + Color4f max = params.get_input("Max_003"); if (operation == NODE_CLAMP_RANGE) { if (min.r > max.r) { std::swap(min.r, max.r); @@ -233,9 +230,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam std::swap(min.a, max.a); } } - MutableSpan results = attribute_result.as_span(); - clamp_attribute( - attribute_input->typed(), results, min, max); + MutableSpan results = attribute_result.as_span(); + clamp_attribute(attribute_input->typed(), results, min, max); break; } default: { 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 5293dd8c876..b13e82e676d 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 @@ -83,8 +83,8 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon * currently. */ const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - OutputAttribute_Typed attribute_result = - component.attribute_try_get_for_output_only(result_name, result_domain); + OutputAttribute_Typed attribute_result = + component.attribute_try_get_for_output_only(result_name, result_domain); if (!attribute_result) { return; } @@ -92,7 +92,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon GVArray_Typed attribute_in = component.attribute_get_for_read( input_name, result_domain, 0.0f); - MutableSpan results = attribute_result.as_span(); + MutableSpan results = attribute_result.as_span(); ColorBand *color_ramp = &node_storage->color_ramp; parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { 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 57ac68b4cd4..a2ff1668a06 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -131,16 +131,16 @@ static void do_equal_operation_float3(const VArray &input_a, } } -static void do_equal_operation_color4f(const VArray &input_a, - const VArray &input_b, +static void do_equal_operation_color4f(const VArray &input_a, + const VArray &input_b, const float threshold, MutableSpan span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const ColorGeometry4f a = input_a[i]; - const ColorGeometry4f b = input_b[i]; + const Color4f a = input_a[i]; + const Color4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) < threshold_squared; } } @@ -185,16 +185,16 @@ static void do_not_equal_operation_float3(const VArray &input_a, } } -static void do_not_equal_operation_color4f(const VArray &input_a, - const VArray &input_b, +static void do_not_equal_operation_color4f(const VArray &input_a, + const VArray &input_b, const float threshold, MutableSpan span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const ColorGeometry4f a = input_a[i]; - const ColorGeometry4f b = input_b[i]; + const Color4f a = input_a[i]; + const Color4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) >= threshold_squared; } } @@ -287,10 +287,8 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed(), attribute_b->typed(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_equal_operation_color4f(attribute_a->typed(), - attribute_b->typed(), - threshold, - result_span); + do_equal_operation_color4f( + attribute_a->typed(), attribute_b->typed(), threshold, result_span); } else if (input_data_type == CD_PROP_BOOL) { do_equal_operation_bool( @@ -307,10 +305,8 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed(), attribute_b->typed(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_not_equal_operation_color4f(attribute_a->typed(), - attribute_b->typed(), - threshold, - result_span); + do_not_equal_operation_color4f( + attribute_a->typed(), attribute_b->typed(), threshold, result_span); } else if (input_data_type == CD_PROP_BOOL) { do_not_equal_operation_bool( 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 599c9e58e52..2fc86269797 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 @@ -165,10 +165,9 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon } case CD_PROP_COLOR: { const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb; - GVArray_Typed attribute_in = - component.attribute_get_for_read( - input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); - MutableSpan results = attribute_result.as_span(); + GVArray_Typed attribute_in = component.attribute_get_for_read( + input_name, result_domain, Color4f(0.0f, 0.0f, 0.0f, 1.0f)); + MutableSpan results = attribute_result.as_span(); parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { for (const int i : range) { BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); 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 389abe3b2aa..60522fd0f72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -110,7 +110,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams break; } case CD_PROP_COLOR: { - const ColorGeometry4f value = params.get_input("Value_002"); + const Color4f value = params.get_input("Value_002"); attribute->fill(&value); break; } 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 a6bd6c0ee32..e502a183ef5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -120,16 +120,16 @@ static void do_mix_operation_float3(const int blend_mode, static void do_mix_operation_color4f(const int blend_mode, const VArray &factors, - const VArray &inputs_a, - const VArray &inputs_b, - VMutableArray &results) + const VArray &inputs_a, + const VArray &inputs_b, + VMutableArray &results) { const int size = results.size(); parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float factor = factors[i]; - ColorGeometry4f a = inputs_a[i]; - const ColorGeometry4f b = inputs_b[i]; + Color4f a = inputs_a[i]; + const Color4f b = inputs_b[i]; ramp_blend(blend_mode, a, factor, b); results.set(i, a); } @@ -160,9 +160,9 @@ static void do_mix_operation(const CustomDataType result_type, else if (result_type == CD_PROP_COLOR) { do_mix_operation_color4f(blend_mode, attribute_factor, - attribute_a.typed(), - attribute_b.typed(), - attribute_result.typed()); + attribute_a.typed(), + attribute_b.typed(), + attribute_result.typed()); } } 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 d6b1ad3e9e0..aa558314b9e 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 @@ -79,9 +79,8 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec const AttributeDomain result_domain = get_result_domain( component, result_attribute_name, mapping_name); - OutputAttribute_Typed attribute_out = - component.attribute_try_get_for_output_only(result_attribute_name, - result_domain); + OutputAttribute_Typed attribute_out = + component.attribute_try_get_for_output_only(result_attribute_name, result_domain); if (!attribute_out) { return; } @@ -89,7 +88,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec GVArray_Typed mapping_attribute = component.attribute_get_for_read( mapping_name, result_domain, {0, 0, 0}); - MutableSpan colors = attribute_out.as_span(); + MutableSpan colors = attribute_out.as_span(); parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { for (const int i : range) { TexResult texture_result = {0}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 049ba5d3143..e0daae0c172 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -134,7 +134,7 @@ static void geo_node_switch_exec(GeoNodeExecParams params) break; } case SOCK_RGBA: { - output_input(params, input, "_004", "Output_004"); + output_input(params, input, "_004", "Output_004"); break; } case SOCK_STRING: { diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 188d198e159..73a702c753a 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -104,10 +104,9 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, return std::make_unique(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_RGBA) { - const ColorGeometry4f value = this->get_input(found_socket->identifier); + const Color4f value = this->get_input(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); - conversions.convert_to_uninitialized( - CPPType::get(), *cpp_type, &value, buffer); + conversions.convert_to_uninitialized(CPPType::get(), *cpp_type, &value, buffer); return std::make_unique(*cpp_type, domain_size, buffer); } BLI_assert(false); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 783a7a9b3d8..ce2848b52a0 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -637,9 +637,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; + *(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; return socktype; } diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc index 220e5ea9046..63f7b8a9ee8 100644 --- a/source/blender/nodes/intern/type_conversions.cc +++ b/source/blender/nodes/intern/type_conversions.cc @@ -66,9 +66,9 @@ static bool float_to_bool(const float &a) { return a > 0.0f; } -static ColorGeometry4f float_to_color(const float &a) +static Color4f float_to_color(const float &a) { - return ColorGeometry4f(a, a, a, 1.0f); + return Color4f(a, a, a, 1.0f); } static float3 float2_to_float3(const float2 &a) @@ -87,9 +87,9 @@ static bool float2_to_bool(const float2 &a) { return !is_zero_v2(a); } -static ColorGeometry4f float2_to_color(const float2 &a) +static Color4f float2_to_color(const float2 &a) { - return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f); + return Color4f(a.x, a.y, 0.0f, 1.0f); } static bool float3_to_bool(const float3 &a) @@ -108,9 +108,9 @@ static float2 float3_to_float2(const float3 &a) { return float2(a); } -static ColorGeometry4f float3_to_color(const float3 &a) +static Color4f float3_to_color(const float3 &a) { - return ColorGeometry4f(a.x, a.y, a.z, 1.0f); + return Color4f(a.x, a.y, a.z, 1.0f); } static bool int_to_bool(const int32_t &a) @@ -129,9 +129,9 @@ static float3 int_to_float3(const int32_t &a) { return float3((float)a); } -static ColorGeometry4f int_to_color(const int32_t &a) +static Color4f int_to_color(const int32_t &a) { - return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); + return Color4f((float)a, (float)a, (float)a, 1.0f); } static float bool_to_float(const bool &a) @@ -150,28 +150,28 @@ static float3 bool_to_float3(const bool &a) { return (a) ? float3(1.0f) : float3(0.0f); } -static ColorGeometry4f bool_to_color(const bool &a) +static Color4f bool_to_color(const bool &a) { - return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f); + return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f); } -static bool color_to_bool(const ColorGeometry4f &a) +static bool color_to_bool(const Color4f &a) { return rgb_to_grayscale(a) > 0.0f; } -static float color_to_float(const ColorGeometry4f &a) +static float color_to_float(const Color4f &a) { return rgb_to_grayscale(a); } -static int32_t color_to_int(const ColorGeometry4f &a) +static int32_t color_to_int(const Color4f &a) { return (int)rgb_to_grayscale(a); } -static float2 color_to_float2(const ColorGeometry4f &a) +static float2 color_to_float2(const Color4f &a) { return float2(a.r, a.g); } -static float3 color_to_float3(const ColorGeometry4f &a) +static float3 color_to_float3(const Color4f &a) { return float3(a.r, a.g, a.b); } @@ -184,37 +184,37 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); return conversions; } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index a7239154633..8ca4a6bab5f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -70,7 +70,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { static blender::fn::MFSignature create_signature() { blender::fn::MFSignatureBuilder signature{"Separate RGB"}; - signature.single_input("Color"); + signature.single_input("Color"); signature.single_output("R"); signature.single_output("G"); signature.single_output("B"); @@ -81,14 +81,14 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext UNUSED(context)) const override { - const blender::VArray &colors = - params.readonly_single_input(0, "Color"); + const blender::VArray &colors = + params.readonly_single_input(0, "Color"); blender::MutableSpan rs = params.uninitialized_single_output(1, "R"); blender::MutableSpan gs = params.uninitialized_single_output(2, "G"); blender::MutableSpan bs = params.uninitialized_single_output(3, "B"); for (int64_t i : mask) { - blender::ColorGeometry4f color = colors[i]; + blender::Color4f color = colors[i]; rs[i] = color.r; gs[i] = color.g; bs[i] = color.b; @@ -155,9 +155,8 @@ static int gpu_shader_combrgb(GPUMaterial *mat, static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO fn{ - "Combine RGB", - [](float r, float g, float b) { return blender::ColorGeometry4f(r, g, b, 1.0f); }}; + static blender::fn::CustomMF_SI_SI_SI_SO fn{ + "Combine RGB", [](float r, float g, float b) { return blender::Color4f(r, g, b, 1.0f); }}; builder.set_matching_fn(fn); } diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 5b2eb300aac..90e8161c09f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -140,7 +140,7 @@ class ColorBandFunction : public blender::fn::MultiFunction { { blender::fn::MFSignatureBuilder signature{"Color Band"}; signature.single_input("Value"); - signature.single_output("Color"); + signature.single_output("Color"); signature.single_output("Alpha"); return signature.build(); } @@ -150,12 +150,12 @@ class ColorBandFunction : public blender::fn::MultiFunction { blender::fn::MFContext UNUSED(context)) const override { const blender::VArray &values = params.readonly_single_input(0, "Value"); - blender::MutableSpan colors = - params.uninitialized_single_output(1, "Color"); + blender::MutableSpan colors = + params.uninitialized_single_output(1, "Color"); blender::MutableSpan alphas = params.uninitialized_single_output(2, "Alpha"); for (int64_t i : mask) { - blender::ColorGeometry4f color; + blender::Color4f color; BKE_colorband_evaluate(&color_band_, values[i], color); colors[i] = color; alphas[i] = color.a; -- cgit v1.2.3 From cb8a6814fdb7755c6f799b64727f078f0076506e Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 25 May 2021 17:16:35 +0200 Subject: Blenlib: Explicit Colors. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Colors are often thought of as being 4 values that make up that can make any color. But that is of course too limited. In C we didn’t spend time to annotate what we meant when using colors. Recently `BLI_color.hh` was made to facilitate color structures in CPP. CPP has possibilities to enforce annotating structures during compilation and can adds conversions between them using function overloading and explicit constructors. The storage structs can hold 4 channels (r, g, b and a). Usage: Convert a theme byte color to a linearrgb premultiplied. ``` ColorTheme4b theme_color; ColorSceneLinear4f linearrgb_color = BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha(); ``` The API is structured to make most use of inlining. Most notable are space conversions done via `BLI_color_convert_to*` functions. - Conversions between spaces (theme <=> scene linear) should always be done by invoking the `BLI_color_convert_to*` methods. - Encoding colors (compressing to store colors inside a less precision storage) should be done by invoking the `encode` and `decode` methods. - Changing alpha association should be done by invoking `premultiply_alpha` or `unpremultiply_alpha` methods. # Encoding. Color encoding is used to store colors with less precision as in using `uint8_t` in stead of `float`. This encoding is supported for `eSpace::SceneLinear`. To make this clear to the developer the `eSpace::SceneLinearByteEncoded` space is added. # Precision Colors can be stored using `uint8_t` or `float` colors. The conversion between the two precisions are available as methods. (`to_4b` and `to_4f`). # Alpha conversion Alpha conversion is only supported in SceneLinear space. Extending: - This file can be extended with `ColorHex/Hsl/Hsv` for different representations of rgb based colors. `ColorHsl4f` - Add non RGB spaces/storages ColorXyz. Reviewed By: JacquesLucke, brecht Differential Revision: https://developer.blender.org/D10978 --- source/blender/blenkernel/BKE_attribute_math.hh | 35 ++- .../blender/blenkernel/intern/attribute_access.cc | 8 +- source/blender/blenkernel/intern/attribute_math.cc | 15 +- .../blenkernel/intern/geometry_component_mesh.cc | 20 +- source/blender/blenlib/BLI_color.hh | 302 ++++++++++++++++++--- source/blender/blenlib/CMakeLists.txt | 2 + source/blender/blenlib/intern/BLI_color.cc | 55 ++++ source/blender/blenlib/tests/BLI_color_test.cc | 133 +++++++++ source/blender/draw/intern/draw_cache_impl.h | 1 - .../space_spreadsheet/spreadsheet_cell_value.hh | 2 +- .../spreadsheet_data_source_geometry.cc | 2 +- .../space_spreadsheet/spreadsheet_layout.cc | 2 +- source/blender/functions/intern/cpp_types.cc | 4 +- .../geometry/nodes/node_geo_attribute_clamp.cc | 16 +- .../nodes/node_geo_attribute_color_ramp.cc | 6 +- .../geometry/nodes/node_geo_attribute_compare.cc | 28 +- .../geometry/nodes/node_geo_attribute_curve_map.cc | 7 +- .../geometry/nodes/node_geo_attribute_fill.cc | 2 +- .../nodes/geometry/nodes/node_geo_attribute_mix.cc | 16 +- .../nodes/node_geo_attribute_sample_texture.cc | 7 +- .../nodes/geometry/nodes/node_geo_switch.cc | 2 +- source/blender/nodes/intern/node_geometry_exec.cc | 5 +- source/blender/nodes/intern/node_socket.cc | 4 +- source/blender/nodes/intern/type_conversions.cc | 50 ++-- .../nodes/shader/nodes/node_shader_curves.cc | 12 +- .../nodes/shader/nodes/node_shader_sepcombRGB.cc | 13 +- .../nodes/shader/nodes/node_shader_valToRgb.cc | 8 +- 27 files changed, 600 insertions(+), 157 deletions(-) create mode 100644 source/blender/blenlib/intern/BLI_color.cc create mode 100644 source/blender/blenlib/tests/BLI_color_test.cc diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 0afdc436415..ba683362e69 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -52,7 +52,7 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f func(bool()); break; case CD_PROP_COLOR: - func(Color4f()); + func(ColorGeometry4f()); break; default: BLI_assert_unreachable(); @@ -78,8 +78,8 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func else if (cpp_type.is()) { func(bool()); } - else if (cpp_type.is()) { - func(Color4f()); + else if (cpp_type.is()) { + func(ColorGeometry4f()); } else { BLI_assert_unreachable(); @@ -123,9 +123,12 @@ inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, co } template<> -inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1, const Color4f &v2) +inline ColorGeometry4f mix3(const float3 &weights, + const ColorGeometry4f &v0, + const ColorGeometry4f &v1, + const ColorGeometry4f &v2) { - Color4f result; + ColorGeometry4f result; interp_v4_v4v4v4(result, v0, v1, v2, weights); return result; } @@ -165,9 +168,10 @@ template<> inline float3 mix2(const float factor, const float3 &a, const float3 return float3::interpolate(a, b, factor); } -template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b) +template<> +inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b) { - Color4f result; + ColorGeometry4f result; interp_v4_v4v4(result, a, b, factor); return result; } @@ -274,15 +278,16 @@ class SimpleMixerWithAccumulationType { } }; -class Color4fMixer { +class ColorGeometryMixer { private: - MutableSpan buffer_; - Color4f default_color_; + MutableSpan buffer_; + ColorGeometry4f default_color_; Array total_weights_; public: - Color4fMixer(MutableSpan buffer, Color4f default_color = {0, 0, 0, 1}); - void mix_in(const int64_t index, const Color4f &color, const float weight = 1.0f); + ColorGeometryMixer(MutableSpan buffer, + ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + void mix_in(const int64_t index, const ColorGeometry4f &color, const float weight = 1.0f); void finalize(); }; @@ -299,10 +304,10 @@ template<> struct DefaultMixerStruct { template<> struct DefaultMixerStruct { using type = SimpleMixer; }; -template<> struct DefaultMixerStruct { - /* Use a special mixer for colors. Color4f can't be added/multiplied, because this is not +template<> struct DefaultMixerStruct { + /* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not * something one should usually do with colors. */ - using type = Color4fMixer; + using type = ColorGeometryMixer; }; template<> struct DefaultMixerStruct { static int double_to_int(const double &value) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 62833e10438..d36e9ed3e86 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -61,7 +61,7 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty case CD_PROP_INT32: return &CPPType::get(); case CD_PROP_COLOR: - return &CPPType::get(); + return &CPPType::get(); case CD_PROP_BOOL: return &CPPType::get(); default: @@ -84,7 +84,7 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) if (type.is()) { return CD_PROP_INT32; } - if (type.is()) { + if (type.is()) { return CD_PROP_COLOR; } if (type.is()) { @@ -355,7 +355,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( case CD_PROP_INT32: return this->layer_to_read_attribute(layer, domain_size); case CD_PROP_COLOR: - return this->layer_to_read_attribute(layer, domain_size); + return this->layer_to_read_attribute(layer, domain_size); case CD_PROP_BOOL: return this->layer_to_read_attribute(layer, domain_size); default: @@ -389,7 +389,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( case CD_PROP_INT32: return this->layer_to_write_attribute(layer, domain_size); case CD_PROP_COLOR: - return this->layer_to_write_attribute(layer, domain_size); + return this->layer_to_write_attribute(layer, domain_size); case CD_PROP_BOOL: return this->layer_to_write_attribute(layer, domain_size); default: diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc index 4ff3a6ceff5..5cdf329effb 100644 --- a/source/blender/blenkernel/intern/attribute_math.cc +++ b/source/blender/blenkernel/intern/attribute_math.cc @@ -18,18 +18,21 @@ namespace blender::attribute_math { -Color4fMixer::Color4fMixer(MutableSpan output_buffer, Color4f default_color) +ColorGeometryMixer::ColorGeometryMixer(MutableSpan output_buffer, + ColorGeometry4f default_color) : buffer_(output_buffer), default_color_(default_color), total_weights_(output_buffer.size(), 0.0f) { - buffer_.fill(Color4f(0, 0, 0, 0)); + buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); } -void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float weight) +void ColorGeometryMixer::mix_in(const int64_t index, + const ColorGeometry4f &color, + const float weight) { BLI_assert(weight >= 0.0f); - Color4f &output_color = buffer_[index]; + ColorGeometry4f &output_color = buffer_[index]; output_color.r += color.r * weight; output_color.g += color.g * weight; output_color.b += color.b * weight; @@ -37,11 +40,11 @@ void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float total_weights_[index] += weight; } -void Color4fMixer::finalize() +void ColorGeometryMixer::finalize() { for (const int64_t i : buffer_.index_range()) { const float weight = total_weights_[i]; - Color4f &output_color = buffer_[i]; + ColorGeometry4f &output_color = buffer_[i]; if (weight > 0.0f) { const float weight_inv = 1.0f / weight; output_color.r *= weight_inv; diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 9e622ab2cdf..42f3a854aec 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -773,18 +773,20 @@ static void set_loop_uv(MLoopUV &uv, float2 co) copy_v2_v2(uv.uv, co); } -static Color4f get_loop_color(const MLoopCol &col) +static ColorGeometry4f get_loop_color(const MLoopCol &col) { - Color4f srgb_color; - rgba_uchar_to_float(srgb_color, &col.r); - Color4f linear_color; - srgb_to_linearrgb_v4(linear_color, srgb_color); + ColorGeometry4b encoded_color = ColorGeometry4b(col.r, col.g, col.b, col.a); + ColorGeometry4f linear_color = encoded_color.decode(); return linear_color; } -static void set_loop_color(MLoopCol &col, Color4f linear_color) +static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color) { - linearrgb_to_srgb_uchar4(&col.r, linear_color); + ColorGeometry4b encoded_color = linear_color.encode(); + col.r = encoded_color.r; + col.g = encoded_color.g; + col.b = encoded_color.b; + col.a = encoded_color.a; } static float get_crease(const MEdge &edge) @@ -1121,8 +1123,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_COLOR, CD_MLOOPCOL, corner_access, - make_derived_read_attribute, - make_derived_write_attribute); + make_derived_read_attribute, + make_derived_write_attribute); static VertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index e57a5109a66..287587e04be 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -22,41 +22,122 @@ namespace blender { -struct Color4f { - float r, g, b, a; +/** + * CPP based color structures. + * + * Strongly typed color storage structures with space and alpha association. + * Will increase readability and visibility of typical mistakes when + * working with colors. + * + * The storage structs can hold 4 channels (r, g, b and a). + * + * Usage: + * + * Convert a theme byte color to a linearrgb premultiplied. + * ``` + * ColorTheme4b theme_color; + * ColorSceneLinear4f linearrgb_color = + * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha(); + * ``` + * + * The API is structured to make most use of inlining. Most notable are space + * conversions done via `BLI_color_convert_to*` functions. + * + * - Conversions between spaces (theme <=> scene linear) should always be done by + * invoking the `BLI_color_convert_to*` methods. + * - Encoding colors (compressing to store colors inside a less precision storage) + * should be done by invoking the `encode` and `decode` methods. + * - Changing alpha association should be done by invoking `premultiply_alpha` or + * `unpremultiply_alpha` methods. + * + * # Encoding. + * + * Color encoding is used to store colors with less precision as in using `uint8_t` in + * stead of `float`. This encoding is supported for `eSpace::SceneLinear`. + * To make this clear to the developer the `eSpace::SceneLinearByteEncoded` + * space is added. + * + * # Precision + * + * Colors can be stored using `uint8_t` or `float` colors. The conversion + * between the two precisions are available as methods. (`to_4b` and + * `to_4f`). + * + * # Alpha conversion + * + * Alpha conversion is only supported in SceneLinear space. + * + * Extending this file: + * - This file can be extended with `ColorHex/Hsl/Hsv` for different representations + * of rgb based colors. `ColorHsl4f` + * - Add non RGB spaces/storages ColorXyz. + */ + +/* Enumeration containing the different alpha modes. */ +enum class eAlpha { + /* Color and alpha are unassociated. */ + Straight, + /* Color and alpha are associated. */ + Premultiplied, +}; +std::ostream &operator<<(std::ostream &stream, const eAlpha &space); - Color4f() = default; +/* Enumeration containing internal spaces. */ +enum class eSpace { + /* Blender theme color space (sRGB). */ + Theme, + /* Blender internal scene linear color space (maps to SceneReference role in OCIO). */ + SceneLinear, + /* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */ + SceneLinearByteEncoded, +}; +std::ostream &operator<<(std::ostream &stream, const eSpace &space); - Color4f(const float *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) +/* Template class to store RGBA values with different precision, space and alpha association. */ +template class ColorRGBA { + public: + ChannelStorageType r, g, b, a; + constexpr ColorRGBA() = default; + + constexpr ColorRGBA(const ChannelStorageType rgba[4]) + : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) { } - Color4f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) + constexpr ColorRGBA(const ChannelStorageType r, + const ChannelStorageType g, + const ChannelStorageType b, + const ChannelStorageType a) + : r(r), g(g), b(b), a(a) { } - operator float *() + operator ChannelStorageType *() { return &r; } - operator const float *() const + operator const ChannelStorageType *() const { return &r; } - friend std::ostream &operator<<(std::ostream &stream, Color4f c) + friend std::ostream &operator<<(std::ostream &stream, + const ColorRGBA &c) { - stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; + + stream << Space << Alpha << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; return stream; } - friend bool operator==(const Color4f &a, const Color4f &b) + friend bool operator==(const ColorRGBA &a, + const ColorRGBA &b) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - friend bool operator!=(const Color4f &a, const Color4f &b) + friend bool operator!=(const ColorRGBA &a, + const ColorRGBA &b) { return !(a == b); } @@ -71,58 +152,209 @@ struct Color4f { } }; -struct Color4b { - uint8_t r, g, b, a; +/* Forward declarations of concrete color classes. */ +template class ColorSceneLinear4f; +template class ColorSceneLinearByteEncoded4b; +template class ColorTheme4; - Color4b() = default; +/* Forward declation of precision conversion methods. */ +BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4f(const ColorTheme4 &srgb4b); +BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4b(const ColorTheme4 &srgb4f); - Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) +template +class ColorSceneLinear4f final : public ColorRGBA { + public: + constexpr ColorSceneLinear4f() : ColorRGBA() { } - Color4b(Color4f other) + constexpr ColorSceneLinear4f(const float *rgba) + : ColorRGBA(rgba) { - rgba_float_to_uchar(*this, other); } - operator Color4f() const + constexpr ColorSceneLinear4f(float r, float g, float b, float a) + : ColorRGBA(r, g, b, a) { - Color4f result; - rgba_uchar_to_float(result, *this); - return result; } - operator uint8_t *() + /** + * Convert to its byte encoded counter space. + **/ + ColorSceneLinearByteEncoded4b encode() const { - return &r; + ColorSceneLinearByteEncoded4b encoded; + linearrgb_to_srgb_uchar4(encoded, *this); + return encoded; } - operator const uint8_t *() const + /** + * Convert color and alpha association to premultiplied alpha. + * + * Does nothing when color has already a premultiplied alpha. + */ + ColorSceneLinear4f premultiply_alpha() const { - return &r; + if constexpr (Alpha == eAlpha::Straight) { + ColorSceneLinear4f premultiplied; + straight_to_premul_v4_v4(premultiplied, *this); + return premultiplied; + } + else { + return *this; + } } - friend std::ostream &operator<<(std::ostream &stream, Color4b c) + /** + * Convert color and alpha association to straight alpha. + * + * Does nothing when color has straighten alpha. + */ + ColorSceneLinear4f unpremultiply_alpha() const { - stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; - return stream; + if constexpr (Alpha == eAlpha::Premultiplied) { + ColorSceneLinear4f straighten; + premul_to_straight_v4_v4(straighten, *this); + return straighten; + } + else { + return *this; + } } +}; - friend bool operator==(const Color4b &a, const Color4b &b) +template +class ColorSceneLinearByteEncoded4b final + : public ColorRGBA { + public: + constexpr ColorSceneLinearByteEncoded4b() = default; + + constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba) + : ColorRGBA(rgba) { - return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - friend bool operator!=(const Color4b &a, const Color4b &b) + constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + : ColorRGBA(r, g, b, a) { - return !(a == b); } - uint64_t hash() const + /** + * Convert to back to float color. + */ + ColorSceneLinear4f decode() const + { + ColorSceneLinear4f decoded; + srgb_to_linearrgb_uchar4(decoded, *this); + return decoded; + } +}; + +/** + * Theme color template class. + * + * Don't use directly, but use `ColorTheme4b/ColorTheme4b`. + * + * This has been implemented as a template to improve inlining. When implemented as concrete + * classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be + * inlined. + */ +template +class ColorTheme4 final : public ColorRGBA { + public: + constexpr ColorTheme4() : ColorRGBA(){}; + + constexpr ColorTheme4(const ChannelStorageType *rgba) + : ColorRGBA(rgba) + { + } + + constexpr ColorTheme4(ChannelStorageType r, + ChannelStorageType g, + ChannelStorageType b, + ChannelStorageType a) + : ColorRGBA(r, g, b, a) + { + } + + /** + * Change precision of color to float. + */ + ColorTheme4 to_4f() const { - return static_cast(r * 1283591) ^ static_cast(g * 850177) ^ - static_cast(b * 735391) ^ static_cast(a * 442319); + if constexpr ((std::is_same_v)) { + return BLI_color_convert_to_theme4f(*this); + } + else { + return *this; + } + } + + /** + * Change precision of color to uint8_t. + */ + ColorTheme4 to_4b() const + { + if constexpr ((std::is_same_v)) { + return BLI_color_convert_to_theme4b(*this); + } + else { + return *this; + } } }; +using ColorTheme4b = ColorTheme4; +using ColorTheme4f = ColorTheme4; + +BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorTheme4f &theme4f) +{ + ColorTheme4b theme4b; + rgba_float_to_uchar(theme4b, theme4f); + return theme4b; +} + +BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorTheme4b &theme4b) +{ + ColorTheme4f theme4f; + rgba_uchar_to_float(theme4f, theme4b); + return theme4f; +} + +BLI_INLINE ColorSceneLinear4f BLI_color_convert_to_scene_linear( + const ColorTheme4f &theme4f) +{ + ColorSceneLinear4f scene_linear; + srgb_to_linearrgb_v4(scene_linear, theme4f); + return scene_linear; +} + +BLI_INLINE ColorSceneLinear4f BLI_color_convert_to_scene_linear( + const ColorTheme4b &theme4b) +{ + ColorSceneLinear4f scene_linear; + srgb_to_linearrgb_uchar4(scene_linear, theme4b); + return scene_linear; +} + +BLI_INLINE ColorTheme4f +BLI_color_convert_to_theme4f(const ColorSceneLinear4f &scene_linear) +{ + ColorTheme4f theme4f; + linearrgb_to_srgb_v4(theme4f, scene_linear); + return theme4f; +} + +BLI_INLINE ColorTheme4b +BLI_color_convert_to_theme4b(const ColorSceneLinear4f &scene_linear) +{ + ColorTheme4b theme4b; + linearrgb_to_srgb_uchar4(theme4b, scene_linear); + return theme4b; +} + +/* Internal roles. For convenience to shorten the type names and hide complexity. */ +using ColorGeometry4f = ColorSceneLinear4f; +using ColorGeometry4b = ColorSceneLinearByteEncoded4b; + } // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index ce3515ac153..f3dc343ee20 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC intern/BLI_args.c intern/BLI_array.c intern/BLI_assert.c + intern/BLI_color.cc intern/BLI_dial_2d.c intern/BLI_dynstr.c intern/BLI_filelist.c @@ -389,6 +390,7 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_color_test.cc tests/BLI_delaunay_2d_test.cc tests/BLI_disjoint_set_test.cc tests/BLI_edgehash_test.cc diff --git a/source/blender/blenlib/intern/BLI_color.cc b/source/blender/blenlib/intern/BLI_color.cc new file mode 100644 index 00000000000..6dcef4f4688 --- /dev/null +++ b/source/blender/blenlib/intern/BLI_color.cc @@ -0,0 +1,55 @@ +/* + * 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_color.hh" + +namespace blender { + +std::ostream &operator<<(std::ostream &stream, const eAlpha &space) +{ + switch (space) { + case eAlpha::Straight: { + stream << "Straight"; + break; + } + case eAlpha::Premultiplied: { + stream << "Premultiplied"; + break; + } + } + return stream; +} + +std::ostream &operator<<(std::ostream &stream, const eSpace &space) +{ + switch (space) { + case eSpace::Theme: { + stream << "Theme"; + break; + } + case eSpace::SceneLinear: { + stream << "SceneLinear"; + break; + } + case eSpace::SceneLinearByteEncoded: { + stream << "SceneLinearByteEncoded"; + break; + } + } + return stream; +} + +} // namespace blender diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc new file mode 100644 index 00000000000..14796e6bf71 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_color_test.cc @@ -0,0 +1,133 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_color.hh" + +namespace blender::tests { + +/** + * \name Conversions + * \{ */ + +TEST(color, ThemeByteToFloat) +{ + ColorTheme4b theme_byte(192, 128, 64, 128); + ColorTheme4f theme_float = theme_byte.to_4f(); + EXPECT_NEAR(0.75f, theme_float.r, 0.01f); + EXPECT_NEAR(0.5f, theme_float.g, 0.01f); + EXPECT_NEAR(0.25f, theme_float.b, 0.01f); + EXPECT_NEAR(0.5f, theme_float.a, 0.01f); +} + +TEST(color, SrgbStraightFloatToByte) +{ + ColorTheme4f theme_float(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme_byte = theme_float.to_4b(); + EXPECT_EQ(191, theme_byte.r); + EXPECT_EQ(128, theme_byte.g); + EXPECT_EQ(64, theme_byte.b); + EXPECT_EQ(128, theme_byte.a); +} + +TEST(color, SrgbStraightToSceneLinearPremultiplied) +{ + BLI_init_srgb_conversion(); + + ColorTheme4b theme(192, 128, 64, 128); + ColorSceneLinear4f linear = + BLI_color_convert_to_scene_linear(theme).premultiply_alpha(); + EXPECT_NEAR(0.26f, linear.r, 0.01f); + EXPECT_NEAR(0.11f, linear.g, 0.01f); + EXPECT_NEAR(0.02f, linear.b, 0.01f); + EXPECT_NEAR(0.5f, linear.a, 0.01f); +} + +TEST(color, SceneLinearStraightToPremultiplied) +{ + ColorSceneLinear4f straight(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinear4f premultiplied = straight.premultiply_alpha(); + EXPECT_NEAR(0.37f, premultiplied.r, 0.01f); + EXPECT_NEAR(0.25f, premultiplied.g, 0.01f); + EXPECT_NEAR(0.12f, premultiplied.b, 0.01f); + EXPECT_NEAR(0.5f, premultiplied.a, 0.01f); +} + +TEST(color, SceneLinearPremultipliedToStraight) +{ + ColorSceneLinear4f premultiplied(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinear4f straight = premultiplied.unpremultiply_alpha(); + EXPECT_NEAR(1.5f, straight.r, 0.01f); + EXPECT_NEAR(1.0f, straight.g, 0.01f); + EXPECT_NEAR(0.5f, straight.b, 0.01f); + EXPECT_NEAR(0.5f, straight.a, 0.01f); +} + +TEST(color, SceneLinearStraightSrgbFloat) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4f theme = BLI_color_convert_to_theme4f(linear); + EXPECT_NEAR(0.88f, theme.r, 0.01); + EXPECT_NEAR(0.73f, theme.g, 0.01); + EXPECT_NEAR(0.53f, theme.b, 0.01); + EXPECT_NEAR(0.5f, theme.a, 0.01); +} + +TEST(color, SceneLinearPremultipliedToSrgbFloat) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4f theme = BLI_color_convert_to_theme4f(linear.unpremultiply_alpha()); + + EXPECT_NEAR(1.19f, theme.r, 0.01); + EXPECT_NEAR(1.0f, theme.g, 0.01); + EXPECT_NEAR(0.74f, theme.b, 0.01); + EXPECT_NEAR(0.5f, theme.a, 0.01); +} + +TEST(color, SceneLinearStraightSrgbByte) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme = BLI_color_convert_to_theme4b(linear); + EXPECT_EQ(225, theme.r); + EXPECT_EQ(188, theme.g); + EXPECT_EQ(137, theme.b); + EXPECT_EQ(128, theme.a); +} + +TEST(color, SceneLinearPremultipliedToSrgbByte) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme = BLI_color_convert_to_theme4b(linear.unpremultiply_alpha()); + EXPECT_EQ(255, theme.r); + EXPECT_EQ(255, theme.g); + EXPECT_EQ(188, theme.b); + EXPECT_EQ(128, theme.a); +} + +TEST(color, SceneLinearByteEncoding) +{ + ColorSceneLinear4f linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinearByteEncoded4b encoded = linear.encode(); + EXPECT_EQ(225, encoded.r); + EXPECT_EQ(188, encoded.g); + EXPECT_EQ(137, encoded.b); + EXPECT_EQ(128, encoded.a); +} + +TEST(color, SceneLinearByteDecoding) +{ + ColorSceneLinearByteEncoded4b encoded(225, 188, 137, 128); + ColorSceneLinear4f decoded = encoded.decode(); + EXPECT_NEAR(0.75f, decoded.r, 0.01f); + EXPECT_NEAR(0.5f, decoded.g, 0.01f); + EXPECT_NEAR(0.25f, decoded.b, 0.01f); + EXPECT_NEAR(0.5f, decoded.a, 0.01f); +} + +/* \} */ + +} // namespace blender::tests diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 2c2ab9eaadd..5743f39f7da 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -266,7 +266,6 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_inner_points(struct Object * struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(struct Object *object, struct ParticleSystem *psys, struct PTCacheEdit *edit); - #ifdef __cplusplus } #endif diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index d1e80f1d87e..c9b73aabf96 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -50,7 +50,7 @@ class CellValue { std::optional value_bool; std::optional value_float2; std::optional value_float3; - std::optional value_color; + std::optional value_color; std::optional value_object; std::optional value_collection; }; 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 02ffa1259fc..452885959f6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -116,7 +116,7 @@ std::unique_ptr GeometryDataSource::get_column_values( column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { - Color4f value; + ColorGeometry4f value; varray->get(index, &value); r_cell_value.value_color = value; }, diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index f1ca65817f6..8079763a339 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -170,7 +170,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { this->draw_float_vector(params, Span(&value.x, 3)); } else if (cell_value.value_color.has_value()) { - const Color4f value = *cell_value.value_color; + const ColorGeometry4f value = *cell_value.value_color; this->draw_float_vector(params, Span(&value.r, 4)); } else if (cell_value.value_object.has_value()) { diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index 53c5def57e9..9c2c1621e23 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -34,8 +34,8 @@ MAKE_CPP_TYPE(int32, int32_t) MAKE_CPP_TYPE(uint32, uint32_t) MAKE_CPP_TYPE(uint8, uint8_t) -MAKE_CPP_TYPE(Color4f, blender::Color4f) -MAKE_CPP_TYPE(Color4b, blender::Color4b) +MAKE_CPP_TYPE(ColorGeometry4f, blender::ColorGeometry4f) +MAKE_CPP_TYPE(ColorGeometry4b, blender::ColorGeometry4b) MAKE_CPP_TYPE(string, std::string) 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 21538db5455..71643df1cb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc @@ -101,9 +101,12 @@ template<> inline float3 clamp_value(const float3 val, const float3 min, const f return tmp; } -template<> inline Color4f clamp_value(const Color4f val, const Color4f min, const Color4f max) +template<> +inline ColorGeometry4f clamp_value(const ColorGeometry4f val, + const ColorGeometry4f min, + const ColorGeometry4f max) { - Color4f tmp; + ColorGeometry4f tmp; tmp.r = std::min(std::max(val.r, min.r), max.r); tmp.g = std::min(std::max(val.g, min.g), max.g); tmp.b = std::min(std::max(val.b, min.b), max.b); @@ -214,8 +217,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam break; } case CD_PROP_COLOR: { - Color4f min = params.get_input("Min_003"); - Color4f max = params.get_input("Max_003"); + ColorGeometry4f min = params.get_input("Min_003"); + ColorGeometry4f max = params.get_input("Max_003"); if (operation == NODE_CLAMP_RANGE) { if (min.r > max.r) { std::swap(min.r, max.r); @@ -230,8 +233,9 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam std::swap(min.a, max.a); } } - MutableSpan results = attribute_result.as_span(); - clamp_attribute(attribute_input->typed(), results, min, max); + MutableSpan results = attribute_result.as_span(); + clamp_attribute( + attribute_input->typed(), results, min, max); break; } default: { 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 b13e82e676d..5293dd8c876 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 @@ -83,8 +83,8 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon * currently. */ const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - OutputAttribute_Typed attribute_result = - component.attribute_try_get_for_output_only(result_name, result_domain); + OutputAttribute_Typed attribute_result = + component.attribute_try_get_for_output_only(result_name, result_domain); if (!attribute_result) { return; } @@ -92,7 +92,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon GVArray_Typed attribute_in = component.attribute_get_for_read( input_name, result_domain, 0.0f); - MutableSpan results = attribute_result.as_span(); + MutableSpan results = attribute_result.as_span(); ColorBand *color_ramp = &node_storage->color_ramp; parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { 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 a2ff1668a06..57ac68b4cd4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -131,16 +131,16 @@ static void do_equal_operation_float3(const VArray &input_a, } } -static void do_equal_operation_color4f(const VArray &input_a, - const VArray &input_b, +static void do_equal_operation_color4f(const VArray &input_a, + const VArray &input_b, const float threshold, MutableSpan span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const Color4f a = input_a[i]; - const Color4f b = input_b[i]; + const ColorGeometry4f a = input_a[i]; + const ColorGeometry4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) < threshold_squared; } } @@ -185,16 +185,16 @@ static void do_not_equal_operation_float3(const VArray &input_a, } } -static void do_not_equal_operation_color4f(const VArray &input_a, - const VArray &input_b, +static void do_not_equal_operation_color4f(const VArray &input_a, + const VArray &input_b, const float threshold, MutableSpan span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const Color4f a = input_a[i]; - const Color4f b = input_b[i]; + const ColorGeometry4f a = input_a[i]; + const ColorGeometry4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) >= threshold_squared; } } @@ -287,8 +287,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed(), attribute_b->typed(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_equal_operation_color4f( - attribute_a->typed(), attribute_b->typed(), threshold, result_span); + do_equal_operation_color4f(attribute_a->typed(), + attribute_b->typed(), + threshold, + result_span); } else if (input_data_type == CD_PROP_BOOL) { do_equal_operation_bool( @@ -305,8 +307,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed(), attribute_b->typed(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_not_equal_operation_color4f( - attribute_a->typed(), attribute_b->typed(), threshold, result_span); + do_not_equal_operation_color4f(attribute_a->typed(), + attribute_b->typed(), + threshold, + result_span); } else if (input_data_type == CD_PROP_BOOL) { do_not_equal_operation_bool( 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 2fc86269797..599c9e58e52 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 @@ -165,9 +165,10 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon } case CD_PROP_COLOR: { const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb; - GVArray_Typed attribute_in = component.attribute_get_for_read( - input_name, result_domain, Color4f(0.0f, 0.0f, 0.0f, 1.0f)); - MutableSpan results = attribute_result.as_span(); + GVArray_Typed attribute_in = + component.attribute_get_for_read( + input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + MutableSpan results = attribute_result.as_span(); parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { for (const int i : range) { BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); 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 60522fd0f72..389abe3b2aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -110,7 +110,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams break; } case CD_PROP_COLOR: { - const Color4f value = params.get_input("Value_002"); + const ColorGeometry4f value = params.get_input("Value_002"); attribute->fill(&value); break; } 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 e502a183ef5..a6bd6c0ee32 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -120,16 +120,16 @@ static void do_mix_operation_float3(const int blend_mode, static void do_mix_operation_color4f(const int blend_mode, const VArray &factors, - const VArray &inputs_a, - const VArray &inputs_b, - VMutableArray &results) + const VArray &inputs_a, + const VArray &inputs_b, + VMutableArray &results) { const int size = results.size(); parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float factor = factors[i]; - Color4f a = inputs_a[i]; - const Color4f b = inputs_b[i]; + ColorGeometry4f a = inputs_a[i]; + const ColorGeometry4f b = inputs_b[i]; ramp_blend(blend_mode, a, factor, b); results.set(i, a); } @@ -160,9 +160,9 @@ static void do_mix_operation(const CustomDataType result_type, else if (result_type == CD_PROP_COLOR) { do_mix_operation_color4f(blend_mode, attribute_factor, - attribute_a.typed(), - attribute_b.typed(), - attribute_result.typed()); + attribute_a.typed(), + attribute_b.typed(), + attribute_result.typed()); } } 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 aa558314b9e..d6b1ad3e9e0 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 @@ -79,8 +79,9 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec const AttributeDomain result_domain = get_result_domain( component, result_attribute_name, mapping_name); - OutputAttribute_Typed attribute_out = - component.attribute_try_get_for_output_only(result_attribute_name, result_domain); + OutputAttribute_Typed attribute_out = + component.attribute_try_get_for_output_only(result_attribute_name, + result_domain); if (!attribute_out) { return; } @@ -88,7 +89,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec GVArray_Typed mapping_attribute = component.attribute_get_for_read( mapping_name, result_domain, {0, 0, 0}); - MutableSpan colors = attribute_out.as_span(); + MutableSpan colors = attribute_out.as_span(); parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { for (const int i : range) { TexResult texture_result = {0}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index e0daae0c172..049ba5d3143 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -134,7 +134,7 @@ static void geo_node_switch_exec(GeoNodeExecParams params) break; } case SOCK_RGBA: { - output_input(params, input, "_004", "Output_004"); + output_input(params, input, "_004", "Output_004"); break; } case SOCK_STRING: { diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 73a702c753a..188d198e159 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -104,9 +104,10 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, return std::make_unique(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_RGBA) { - const Color4f value = this->get_input(found_socket->identifier); + const ColorGeometry4f value = this->get_input(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); - conversions.convert_to_uninitialized(CPPType::get(), *cpp_type, &value, buffer); + conversions.convert_to_uninitialized( + CPPType::get(), *cpp_type, &value, buffer); return std::make_unique(*cpp_type, domain_size, buffer); } BLI_assert(false); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index ce2848b52a0..783a7a9b3d8 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -637,9 +637,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_cpp_type = []() { return &blender::fn::CPPType::get(); }; socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; + *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; return socktype; } diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc index 63f7b8a9ee8..220e5ea9046 100644 --- a/source/blender/nodes/intern/type_conversions.cc +++ b/source/blender/nodes/intern/type_conversions.cc @@ -66,9 +66,9 @@ static bool float_to_bool(const float &a) { return a > 0.0f; } -static Color4f float_to_color(const float &a) +static ColorGeometry4f float_to_color(const float &a) { - return Color4f(a, a, a, 1.0f); + return ColorGeometry4f(a, a, a, 1.0f); } static float3 float2_to_float3(const float2 &a) @@ -87,9 +87,9 @@ static bool float2_to_bool(const float2 &a) { return !is_zero_v2(a); } -static Color4f float2_to_color(const float2 &a) +static ColorGeometry4f float2_to_color(const float2 &a) { - return Color4f(a.x, a.y, 0.0f, 1.0f); + return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f); } static bool float3_to_bool(const float3 &a) @@ -108,9 +108,9 @@ static float2 float3_to_float2(const float3 &a) { return float2(a); } -static Color4f float3_to_color(const float3 &a) +static ColorGeometry4f float3_to_color(const float3 &a) { - return Color4f(a.x, a.y, a.z, 1.0f); + return ColorGeometry4f(a.x, a.y, a.z, 1.0f); } static bool int_to_bool(const int32_t &a) @@ -129,9 +129,9 @@ static float3 int_to_float3(const int32_t &a) { return float3((float)a); } -static Color4f int_to_color(const int32_t &a) +static ColorGeometry4f int_to_color(const int32_t &a) { - return Color4f((float)a, (float)a, (float)a, 1.0f); + return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); } static float bool_to_float(const bool &a) @@ -150,28 +150,28 @@ static float3 bool_to_float3(const bool &a) { return (a) ? float3(1.0f) : float3(0.0f); } -static Color4f bool_to_color(const bool &a) +static ColorGeometry4f bool_to_color(const bool &a) { - return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f); + return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f); } -static bool color_to_bool(const Color4f &a) +static bool color_to_bool(const ColorGeometry4f &a) { return rgb_to_grayscale(a) > 0.0f; } -static float color_to_float(const Color4f &a) +static float color_to_float(const ColorGeometry4f &a) { return rgb_to_grayscale(a); } -static int32_t color_to_int(const Color4f &a) +static int32_t color_to_int(const ColorGeometry4f &a) { return (int)rgb_to_grayscale(a); } -static float2 color_to_float2(const Color4f &a) +static float2 color_to_float2(const ColorGeometry4f &a) { return float2(a.r, a.g); } -static float3 color_to_float3(const Color4f &a) +static float3 color_to_float3(const ColorGeometry4f &a) { return float3(a.r, a.g, a.b); } @@ -184,37 +184,37 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); - add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); + add_implicit_conversion(conversions); return conversions; } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index bc99e4b5dd8..f1d5040a292 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -293,8 +293,8 @@ class CurveRGBFunction : public blender::fn::MultiFunction { { blender::fn::MFSignatureBuilder signature{"Curve RGB"}; signature.single_input("Fac"); - signature.single_input("Color"); - signature.single_output("Color"); + signature.single_input("Color"); + signature.single_output("Color"); return signature.build(); } @@ -303,10 +303,10 @@ class CurveRGBFunction : public blender::fn::MultiFunction { blender::fn::MFContext UNUSED(context)) const override { const blender::VArray &fac = params.readonly_single_input(0, "Fac"); - const blender::VArray &col_in = - params.readonly_single_input(1, "Color"); - blender::MutableSpan col_out = - params.uninitialized_single_output(2, "Color"); + const blender::VArray &col_in = + params.readonly_single_input(1, "Color"); + blender::MutableSpan col_out = + params.uninitialized_single_output(2, "Color"); for (int64_t i : mask) { BKE_curvemapping_evaluateRGBF(&cumap_, col_out[i], col_in[i]); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index 8ca4a6bab5f..a7239154633 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -70,7 +70,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { static blender::fn::MFSignature create_signature() { blender::fn::MFSignatureBuilder signature{"Separate RGB"}; - signature.single_input("Color"); + signature.single_input("Color"); signature.single_output("R"); signature.single_output("G"); signature.single_output("B"); @@ -81,14 +81,14 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { blender::fn::MFParams params, blender::fn::MFContext UNUSED(context)) const override { - const blender::VArray &colors = - params.readonly_single_input(0, "Color"); + const blender::VArray &colors = + params.readonly_single_input(0, "Color"); blender::MutableSpan rs = params.uninitialized_single_output(1, "R"); blender::MutableSpan gs = params.uninitialized_single_output(2, "G"); blender::MutableSpan bs = params.uninitialized_single_output(3, "B"); for (int64_t i : mask) { - blender::Color4f color = colors[i]; + blender::ColorGeometry4f color = colors[i]; rs[i] = color.r; gs[i] = color.g; bs[i] = color.b; @@ -155,8 +155,9 @@ static int gpu_shader_combrgb(GPUMaterial *mat, static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO fn{ - "Combine RGB", [](float r, float g, float b) { return blender::Color4f(r, g, b, 1.0f); }}; + static blender::fn::CustomMF_SI_SI_SI_SO fn{ + "Combine RGB", + [](float r, float g, float b) { return blender::ColorGeometry4f(r, g, b, 1.0f); }}; builder.set_matching_fn(fn); } diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 90e8161c09f..5b2eb300aac 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -140,7 +140,7 @@ class ColorBandFunction : public blender::fn::MultiFunction { { blender::fn::MFSignatureBuilder signature{"Color Band"}; signature.single_input("Value"); - signature.single_output("Color"); + signature.single_output("Color"); signature.single_output("Alpha"); return signature.build(); } @@ -150,12 +150,12 @@ class ColorBandFunction : public blender::fn::MultiFunction { blender::fn::MFContext UNUSED(context)) const override { const blender::VArray &values = params.readonly_single_input(0, "Value"); - blender::MutableSpan colors = - params.uninitialized_single_output(1, "Color"); + blender::MutableSpan colors = + params.uninitialized_single_output(1, "Color"); blender::MutableSpan alphas = params.uninitialized_single_output(2, "Alpha"); for (int64_t i : mask) { - blender::Color4f color; + blender::ColorGeometry4f color; BKE_colorband_evaluate(&color_band_, values[i], color); colors[i] = color; alphas[i] = color.a; -- cgit v1.2.3 From 9f60188cd805d10155c79ddb99adb703d6da7ba1 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Tue, 25 May 2021 23:32:04 +0800 Subject: Cleanup: Use ListBase in various places in line art. This clarifies the data structures for storing edges for different calculation stages. Reviewed By: Sebastian Parborg (zeddb) Differential Revision: https://developer.blender.org/D11386 --- .../gpencil_modifiers/intern/lineart/MOD_lineart.h | 54 ++++----------- .../gpencil_modifiers/intern/lineart/lineart_cpu.c | 81 ++++++++++------------ .../intern/lineart/lineart_intern.h | 39 +++++++---- .../intern/lineart/lineart_util.c | 6 +- 4 files changed, 79 insertions(+), 101 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 56b1ff87f0b..e679dce2f2d 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -237,31 +237,14 @@ typedef struct LineartRenderBuffer { int triangle_size; - unsigned int contour_count; - unsigned int contour_processed; - LineartEdge *contour_managed; - /** A single linked list (cast to #LinkNode). */ - LineartEdge *contours; - - unsigned int intersection_count; - unsigned int intersection_processed; - LineartEdge *intersection_managed; - LineartEdge *intersection_lines; - - unsigned int crease_count; - unsigned int crease_processed; - LineartEdge *crease_managed; - LineartEdge *crease_lines; - - unsigned int material_line_count; - unsigned int material_processed; - LineartEdge *material_managed; - LineartEdge *material_lines; - - unsigned int edge_mark_count; - unsigned int edge_mark_processed; - LineartEdge *edge_mark_managed; - LineartEdge *edge_marks; + /* Although using ListBase here, LineartEdge is single linked list. + * list.last is used to store worker progress along the list. + * See lineart_main_occlusion_begin() for more info. */ + ListBase contour; + ListBase intersection; + ListBase crease; + ListBase material; + ListBase edge_mark; ListBase chains; @@ -334,20 +317,13 @@ typedef struct LineartRenderTaskInfo { int thread_id; - LineartEdge *contour; - LineartEdge *contour_end; - - LineartEdge *intersection; - LineartEdge *intersection_end; - - LineartEdge *crease; - LineartEdge *crease_end; - - LineartEdge *material; - LineartEdge *material_end; - - LineartEdge *edge_mark; - LineartEdge *edge_mark_end; + /* These lists only denote the part of the main edge list that the thread should iterate over. + * Be careful to not iterate outside of these bounds as it is not thread safe to do so. */ + ListBase contour; + ListBase intersection; + ListBase crease; + ListBase material; + ListBase edge_mark; } LineartRenderTaskInfo; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index d43b9a3426f..ae8157e1a97 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -376,18 +376,18 @@ static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRend BLI_spin_lock(&rb->lock_task); #define LRT_ASSIGN_OCCLUSION_TASK(name) \ - if (rb->name##_managed) { \ - data = rb->name##_managed; \ - rti->name = (void *)data; \ + if (rb->name.last) { \ + data = rb->name.last; \ + rti->name.first = (void *)data; \ for (i = 0; i < LRT_THREAD_EDGE_COUNT && data; i++) { \ data = data->next; \ } \ - rti->name##_end = data; \ - rb->name##_managed = data; \ + rti->name.last = data; \ + rb->name.last = data; \ res = 1; \ } \ else { \ - rti->name = NULL; \ + rti->name.first = rti->name.last = NULL; \ } LRT_ASSIGN_OCCLUSION_TASK(contour); @@ -410,23 +410,23 @@ static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartR while (lineart_occlusion_make_task_info(rb, rti)) { - for (eip = rti->contour; eip && eip != rti->contour_end; eip = eip->next) { + for (eip = rti->contour.first; eip && eip != rti->contour.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } - for (eip = rti->crease; eip && eip != rti->crease_end; eip = eip->next) { + for (eip = rti->crease.first; eip && eip != rti->crease.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } - for (eip = rti->intersection; eip && eip != rti->intersection_end; eip = eip->next) { + for (eip = rti->intersection.first; eip && eip != rti->intersection.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } - for (eip = rti->material; eip && eip != rti->material_end; eip = eip->next) { + for (eip = rti->material.first; eip && eip != rti->material.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } - for (eip = rti->edge_mark; eip && eip != rti->edge_mark_end; eip = eip->next) { + for (eip = rti->edge_mark.first; eip && eip != rti->edge_mark.last; eip = eip->next) { lineart_occlusion_single_line(rb, eip, rti->thread_id); } } @@ -444,11 +444,13 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) "Task Pool"); int i; - rb->contour_managed = rb->contours; - rb->crease_managed = rb->crease_lines; - rb->intersection_managed = rb->intersection_lines; - rb->material_managed = rb->material_lines; - rb->edge_mark_managed = rb->edge_marks; + /* The "last" entry is used to store worker progress in the whole list. + * These list themselves are single-direction linked, with list.first being the head. */ + rb->contour.last = rb->contour.first; + rb->crease.last = rb->crease.first; + rb->intersection.last = rb->intersection.first; + rb->material.last = rb->material.first; + rb->edge_mark.last = rb->edge_mark.first; TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); @@ -853,7 +855,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } /* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as * `tri->edge` and `tri->v` has the same sequence. and the winding direction @@ -902,7 +904,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } e->v1 = &vt[0]; e->v2 = &vt[1]; @@ -943,7 +945,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1018,7 +1020,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1070,7 +1072,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1118,7 +1120,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1431,19 +1433,19 @@ static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e) { switch (e->flags) { case LRT_EDGE_FLAG_CONTOUR: - lineart_prepend_edge_direct(&rb->contours, e); + lineart_prepend_edge_direct(&rb->contour.first, e); break; case LRT_EDGE_FLAG_CREASE: - lineart_prepend_edge_direct(&rb->crease_lines, e); + lineart_prepend_edge_direct(&rb->crease.first, e); break; case LRT_EDGE_FLAG_MATERIAL: - lineart_prepend_edge_direct(&rb->material_lines, e); + lineart_prepend_edge_direct(&rb->material.first, e); break; case LRT_EDGE_FLAG_EDGE_MARK: - lineart_prepend_edge_direct(&rb->edge_marks, e); + lineart_prepend_edge_direct(&rb->edge_mark.first, e); break; case LRT_EDGE_FLAG_INTERSECTION: - lineart_prepend_edge_direct(&rb->intersection_lines, e); + lineart_prepend_edge_direct(&rb->intersection.first, e); break; } } @@ -2497,7 +2499,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, BLI_addtail(&result->segments, es); /* Don't need to OR flags right now, just a type mark. */ result->flags = LRT_EDGE_FLAG_INTERSECTION; - lineart_prepend_edge_direct(&rb->intersection_lines, result); + lineart_prepend_edge_direct(&rb->intersection.first, result); int r1, r2, c1, c2, row, col; if (lineart_get_edge_bounding_areas(rb, result, &r1, &r2, &c1, &c2)) { for (row = r1; row != r2 + 1; row++) { @@ -2508,8 +2510,6 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, } } - rb->intersection_count++; - return result; } @@ -2598,22 +2598,11 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb) return; } - rb->contour_count = 0; - rb->contour_managed = NULL; - rb->intersection_count = 0; - rb->intersection_managed = NULL; - rb->material_line_count = 0; - rb->material_managed = NULL; - rb->crease_count = 0; - rb->crease_managed = NULL; - rb->edge_mark_count = 0; - rb->edge_mark_managed = NULL; - - rb->contours = NULL; - rb->intersection_lines = NULL; - rb->crease_lines = NULL; - rb->material_lines = NULL; - rb->edge_marks = NULL; + memset(&rb->contour, 0, sizeof(ListBase)); + memset(&rb->crease, 0, sizeof(ListBase)); + memset(&rb->intersection, 0, sizeof(ListBase)); + memset(&rb->edge_mark, 0, sizeof(ListBase)); + memset(&rb->material, 0, sizeof(ListBase)); BLI_listbase_clear(&rb->chains); BLI_listbase_clear(&rb->wasted_cuts); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 47ca6e45bd5..9ed98b38f07 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -58,7 +58,7 @@ void *lineart_mem_acquire(struct LineartStaticMemPool *smp, size_t size); void *lineart_mem_acquire_thread(struct LineartStaticMemPool *smp, size_t size); void lineart_mem_destroy(struct LineartStaticMemPool *smp); -void lineart_prepend_edge_direct(struct LineartEdge **first, void *node); +void lineart_prepend_edge_direct(void **list_head, void *node); void lineart_prepend_pool(LinkNode **first, struct LineartStaticMemPool *smp, void *link); void lineart_matrix_ortho_44d(double (*mProjection)[4], @@ -76,29 +76,42 @@ int lineart_count_intersection_segment_count(struct LineartRenderBuffer *rb); void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb); #define LRT_ITER_ALL_LINES_BEGIN \ - LineartEdge *e, *next_e, **current_list; \ - e = rb->contours; \ - for (current_list = &rb->contours; e; e = next_e) { \ + LineartEdge *e, *next_e; \ + void **current_head; \ + e = rb->contour.first; \ + if (!e) { \ + e = rb->crease.first; \ + } \ + if (!e) { \ + e = rb->material.first; \ + } \ + if (!e) { \ + e = rb->edge_mark.first; \ + } \ + if (!e) { \ + e = rb->intersection.first; \ + } \ + for (current_head = &rb->contour.first; e; e = next_e) { \ next_e = e->next; #define LRT_ITER_ALL_LINES_NEXT \ while (!next_e) { \ - if (current_list == &rb->contours) { \ - current_list = &rb->crease_lines; \ + if (current_head == &rb->contour.first) { \ + current_head = &rb->crease.first; \ } \ - else if (current_list == &rb->crease_lines) { \ - current_list = &rb->material_lines; \ + else if (current_head == &rb->crease.first) { \ + current_head = &rb->material.first; \ } \ - else if (current_list == &rb->material_lines) { \ - current_list = &rb->edge_marks; \ + else if (current_head == &rb->material.first) { \ + current_head = &rb->edge_mark.first; \ } \ - else if (current_list == &rb->edge_marks) { \ - current_list = &rb->intersection_lines; \ + else if (current_head == &rb->edge_mark.first) { \ + current_head = &rb->intersection.first; \ } \ else { \ break; \ } \ - next_e = *current_list; \ + next_e = *current_head; \ } #define LRT_ITER_ALL_LINES_END \ diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c index dcb1f9cde5d..d05f931f75d 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c @@ -135,11 +135,11 @@ void lineart_mem_destroy(LineartStaticMemPool *smp) } } -void lineart_prepend_edge_direct(LineartEdge **first, void *node) +void lineart_prepend_edge_direct(void **list_head, void *node) { LineartEdge *e_n = (LineartEdge *)node; - e_n->next = (*first); - (*first) = e_n; + e_n->next = (*list_head); + (*list_head) = e_n; } void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link) -- cgit v1.2.3 From 45b28c0f88475f0b2d556a237d474c7415344461 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Tue, 25 May 2021 23:45:47 +0800 Subject: GPencil: Add a use_light option when creating object. This option is default off when creating line art objects because line art seldom use lighting and the normal data would be all over the place anyway. Reviewed By: Antonio Vazquez (antoniov) Differential Revision: https://developer.blender.org/D11372 --- source/blender/editors/object/object_add.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 8cf7d60e6c4..b0255e79858 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1312,6 +1312,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) const int type = RNA_enum_get(op->ptr, "type"); const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front"); + const bool use_lights = RNA_boolean_get(op->ptr, "use_lights"); const int stroke_depth_order = RNA_enum_get(op->ptr, "stroke_depth_order"); ushort local_view_bits; @@ -1432,6 +1433,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) id_us_plus(&md->target_material->id); } + if (use_lights) { + ob->dtx |= OB_USE_GPENCIL_LIGHTS; + } + else { + ob->dtx &= ~OB_USE_GPENCIL_LIGHTS; + } + /* Stroke object is drawn in front of meshes by default. */ if (use_in_front) { ob->dtx |= OB_DRAW_IN_FRONT; @@ -1474,6 +1482,7 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op) int type = RNA_enum_get(op->ptr, "type"); if (type == GP_LRT_COLLECTION || type == GP_LRT_OBJECT || type == GP_LRT_SCENE) { + uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE); uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE); bool in_front = RNA_boolean_get(op->ptr, "use_in_front"); uiLayout *row = uiLayoutRow(layout, false); @@ -1520,6 +1529,8 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) false, "In Front", "Show line art grease pencil in front of everything"); + RNA_def_boolean( + ot->srna, "use_lights", false, "Use Lights", "Use lights for this grease pencil object"); RNA_def_enum( ot->srna, "stroke_depth_order", -- cgit v1.2.3 From 490dd279cc7895a005db2cc6189402711685a193 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Tue, 25 May 2021 10:27:48 -0600 Subject: deps: Fix broken boost link --- build_files/build_environment/cmake/versions.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 83e438614b6..88f84614867 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -43,7 +43,7 @@ set(JPEG_FILE libjpeg-turbo-${JPEG_VERSION}.tar.gz) set(BOOST_VERSION 1.73.0) set(BOOST_VERSION_NODOTS 1_73_0) set(BOOST_VERSION_NODOTS_SHORT 1_73) -set(BOOST_URI https://dl.bintray.com/boostorg/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_NODOTS}.tar.gz) +set(BOOST_URI https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_NODOTS}.tar.gz) set(BOOST_HASH 4036cd27ef7548b8d29c30ea10956196) set(BOOST_HASH_TYPE MD5) set(BOOST_FILE boost_${BOOST_VERSION_NODOTS}.tar.gz) -- cgit v1.2.3 From 95690dd362f3a94f6c3b1efbe91e8b5cc164745f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 25 May 2021 18:58:28 +0200 Subject: Bump FFmpeg version from 4.2.3 to 4.4 Bump FFmpeg version to 4.4 to fix a problem where it would write the wrong frame rate. Their old API was deprecated and Blender moved to the new one in rB8d6264ea12bfac0912c7249f00af2ac8e3409ed1. The new one produced files with the wrong frame rate, which was fixed in FFmpeg 4.4. Manifest Task: T88568 Reviewed By: LazyDodo, zeddb Differential Revision: https://developer.blender.org/D11392 --- build_files/build_environment/cmake/versions.cmake | 6 +++--- build_files/build_environment/install_deps.sh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 88f84614867..4500245d42a 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -297,10 +297,10 @@ set(OPENJPEG_HASH 63f5a4713ecafc86de51bfad89cc07bb788e9bba24ebbf0c4ca637621aadb6 set(OPENJPEG_HASH_TYPE SHA256) set(OPENJPEG_FILE openjpeg-v${OPENJPEG_VERSION}.tar.gz) -set(FFMPEG_VERSION 4.2.3) +set(FFMPEG_VERSION 4.4) set(FFMPEG_URI http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2) -set(FFMPEG_HASH 695fad11f3baf27784e24cb0e977b65a) -set(FFMPEG_HASH_TYPE MD5) +set(FFMPEG_HASH 42093549751b582cf0f338a21a3664f52e0a9fbe0d238d3c992005e493607d0e) +set(FFMPEG_HASH_TYPE SHA256) set(FFMPEG_FILE ffmpeg-${FFMPEG_VERSION}.tar.bz2) set(FFTW_VERSION 3.3.8) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 18922799d08..71ddeaa4576 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -563,9 +563,9 @@ OIDN_SKIP=false ISPC_VERSION="1.14.1" -FFMPEG_VERSION="4.2.3" -FFMPEG_VERSION_SHORT="4.2" -FFMPEG_VERSION_MIN="3.0" +FFMPEG_VERSION="4.4" +FFMPEG_VERSION_SHORT="4.4" +FFMPEG_VERSION_MIN="4.4" FFMPEG_VERSION_MAX="5.0" FFMPEG_FORCE_BUILD=false FFMPEG_FORCE_REBUILD=false -- cgit v1.2.3 From c0bb7d9cb78787b006cd71b1d809d6a0e47bb4f2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 25 May 2021 14:37:58 -0400 Subject: Cleanup: Fix short comparison with bool warning For some reason the hide status is stored in a short and a char (we cannot have bools in DNA). --- source/blender/draw/intern/draw_cache_impl_curve.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 223b44724b6..ddafc7205bb 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -796,7 +796,7 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, if (bezt) { for (int a = 0; a < nu->pntsu; a++, bezt++) { - if (bezt->hide == true) { + if (bezt->hide != 0) { continue; } const bool handle_selected = BEZT_ISSEL_ANY(bezt); @@ -831,7 +831,7 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, else if (bp) { int pt_len = nu->pntsu * nu->pntsv; for (int a = 0; a < pt_len; a++, bp++, vbo_len_used += 1) { - if (bp->hide == true) { + if (bp->hide != 0) { continue; } int u = (a % nu->pntsu); -- cgit v1.2.3 From ebde6e1852622d60d1cd574f1c7e2d6855ae90ba Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 25 May 2021 17:46:17 +0200 Subject: Fix T88251: "Operator Cheat Sheet" Crash Caused by {rB919558854d62}. Same fix as in {rBdc8a43c8755a} -- let RNA enum item callbacks check for NULL context. The NULL context is used to extract items for document generation. Maniphest Tasks: T88251 Differential Revision: https://developer.blender.org/D11391 --- source/blender/editors/geometry/geometry_attributes.c | 10 +++++++--- source/blender/editors/object/object_vgroup.c | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/geometry/geometry_attributes.c b/source/blender/editors/geometry/geometry_attributes.c index 12f6bb90677..b2ecee90a57 100644 --- a/source/blender/editors/geometry/geometry_attributes.c +++ b/source/blender/editors/geometry/geometry_attributes.c @@ -52,12 +52,16 @@ static const EnumPropertyItem *geometry_attribute_domain_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { + if (C == NULL) { + return DummyRNA_NULL_items; + } + Object *ob = ED_object_context(C); - if (ob != NULL) { - return rna_enum_attribute_domain_itemf(ob->data, r_free); + if (ob == NULL) { + return DummyRNA_NULL_items; } - return DummyRNA_NULL_items; + return rna_enum_attribute_domain_itemf(ob->data, r_free); } static int geometry_attribute_add_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 5f81f9afe4f..3f40d637188 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -3985,6 +3985,10 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { + if (C == NULL) { + return DummyRNA_NULL_items; + } + Object *ob = ED_object_context(C); EnumPropertyItem tmp = {0, "", 0, "", ""}; EnumPropertyItem *item = NULL; -- cgit v1.2.3 From b813007a0f658cf7949f8b642bcdac75cc4a1dc2 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 25 May 2021 16:27:34 +0200 Subject: Fix T88566: Mantaflow inflow with shapekeys is not working anymore (regression) Code was actually checking for shapekeys, but these were not detected properly (some effects like shape keys are added as virtual modifiers before the user created modifiers) Now go over virtual modifiers as well. Maniphest Tasks: T88566 Differential Revision: https://developer.blender.org/D11388 --- source/blender/blenkernel/intern/fluid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 851d8aae378..1cd5c30c2dd 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -623,7 +623,8 @@ static void clamp_bounds_in_domain(FluidDomainSettings *fds, static bool is_static_object(Object *ob) { /* Check if the object has modifiers that might make the object "dynamic". */ - ModifierData *md = ob->modifiers.first; + VirtualModifierData virtualModifierData; + ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); for (; md; md = md->next) { if (ELEM(md->type, eModifierType_Cloth, -- cgit v1.2.3 From 03c0fa1cdb38695629c76fe6b14f733702b7061a Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 25 May 2021 16:34:10 +0200 Subject: Fix T88531: Mantaflow problem with geometry nodes Objects modified by geometry nodes modifiers were not caught as being "dynamic". Now add this modifier type to the list of modifiers making them "dynamic" in the eyes of mantaflow. (noticed by @sebbas in chat) Maniphest Tasks: T88531 Differential Revision: https://developer.blender.org/D11389 --- source/blender/blenkernel/intern/fluid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 1cd5c30c2dd..493a267c2f0 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -632,7 +632,8 @@ static bool is_static_object(Object *ob) eModifierType_Explode, eModifierType_Ocean, eModifierType_ShapeKey, - eModifierType_Softbody)) { + eModifierType_Softbody, + eModifierType_Nodes)) { return false; } } -- cgit v1.2.3 From 09e77f904d207e1f4b9b02d1282f21a423d9dc00 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 25 May 2021 12:02:57 +0200 Subject: Fix T88534: Unable to add a Geometry Node Tree on Volume object Volumes are supported, poll corrected. Maniphest Tasks: T88534 Differential Revision: https://developer.blender.org/D11378 --- release/scripts/startup/bl_operators/geometry_nodes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index 0c7a2a01b7a..71ef89a066b 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 volume, curve, or hair object support yet) - if not ob or ob.type not in {'MESH', 'POINTCLOUD'}: + # 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'}: return False return True -- cgit v1.2.3 From 1e6b0285804c24ba7a46c10c9a00a865e282363a Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 26 May 2021 10:45:27 +0200 Subject: Cleanup: Remove unused argument in Cycles sync Makes it easier to see where exactly the viewport is used. --- intern/cycles/blender/blender_sync.cpp | 4 ++-- intern/cycles/blender/blender_sync.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 9d0f9f29f94..64a2adccfe6 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -246,7 +246,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); - sync_view_layer(b_v3d, b_view_layer); + sync_view_layer(b_view_layer); sync_integrator(); sync_film(b_v3d); sync_shaders(b_depsgraph, b_v3d); @@ -441,7 +441,7 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d) /* Render Layer */ -void BlenderSync::sync_view_layer(BL::SpaceView3D & /*b_v3d*/, BL::ViewLayer &b_view_layer) +void BlenderSync::sync_view_layer(BL::ViewLayer &b_view_layer) { view_layer.name = b_view_layer.name(); diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 15a10f2b46b..8cd65f13f70 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -73,7 +73,7 @@ class BlenderSync { int width, int height, void **python_thread_state); - void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_layer); + void sync_view_layer(BL::ViewLayer &b_view_layer); vector sync_render_passes(BL::Scene &b_scene, BL::RenderLayer &b_render_layer, BL::ViewLayer &b_view_layer, -- cgit v1.2.3 From e6c0e6c2a93f4fb73988fa0559ba9d88017c36a9 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 26 May 2021 09:28:01 +0200 Subject: Compositor: Use BLI_color in convert alpha node. Recently the CPP colors module landed in master. This patch will use the new module in the convert alpha node. --- .../compositor/operations/COM_ConvertOperation.cc | 35 +++++++--------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 2ea15185c0f..384936533c7 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -18,6 +18,8 @@ #include "COM_ConvertOperation.h" +#include "BLI_color.hh" + #include "IMB_colormanagement.h" namespace blender::compositor { @@ -355,21 +357,10 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - float inputValue[4]; - float alpha; - - this->m_inputOperation->readSampled(inputValue, x, y, sampler); - alpha = inputValue[3]; - - if (fabsf(alpha) < 1e-5f) { - zero_v3(output); - } - else { - mul_v3_v3fl(output, inputValue, 1.0f / alpha); - } - - /* never touches the alpha */ - output[3] = alpha; + ColorSceneLinear4f input; + this->m_inputOperation->readSampled(input, x, y, sampler); + ColorSceneLinear4f converted = input.unpremultiply_alpha(); + copy_v4_v4(output, converted); } /* ******** Straight to Premul ******** */ @@ -385,16 +376,10 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4], float y, PixelSampler sampler) { - float inputValue[4]; - float alpha; - - this->m_inputOperation->readSampled(inputValue, x, y, sampler); - alpha = inputValue[3]; - - mul_v3_v3fl(output, inputValue, alpha); - - /* never touches the alpha */ - output[3] = alpha; + ColorSceneLinear4f input; + this->m_inputOperation->readSampled(input, x, y, sampler); + ColorSceneLinear4f converted = input.premultiply_alpha(); + copy_v4_v4(output, converted); } /* ******** Separate Channels ******** */ -- cgit v1.2.3 From a72a5809481217de6a6440ed80c7a22bb31a227a Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 26 May 2021 11:07:05 +0200 Subject: Cleanup: Simplify Cycles viewport parameters Use early output and access shading RNA object only once. --- intern/cycles/blender/blender_viewport.cpp | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index 73ef5f94720..aac3d41314a 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -32,17 +32,26 @@ BlenderViewportParameters::BlenderViewportParameters() BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) : BlenderViewportParameters() { + if (!b_v3d) { + return; + } + + BL::View3DShading shading = b_v3d.shading(); + /* We only copy the parameters if we are in look dev mode. otherwise * defaults are being used. These defaults mimic normal render settings */ - if (b_v3d && b_v3d.shading().type() == BL::View3DShading::type_RENDERED) { - use_scene_world = b_v3d.shading().use_scene_world_render(); - use_scene_lights = b_v3d.shading().use_scene_lights_render(); - if (!use_scene_world) { - studiolight_rotate_z = b_v3d.shading().studiolight_rotate_z(); - studiolight_intensity = b_v3d.shading().studiolight_intensity(); - studiolight_background_alpha = b_v3d.shading().studiolight_background_alpha(); - studiolight_path = b_v3d.shading().selected_studio_light().path(); - } + if (shading.type() != BL::View3DShading::type_RENDERED) { + return; + } + + use_scene_world = shading.use_scene_world_render(); + use_scene_lights = shading.use_scene_lights_render(); + + if (!use_scene_world) { + studiolight_rotate_z = shading.studiolight_rotate_z(); + studiolight_intensity = shading.studiolight_intensity(); + studiolight_background_alpha = shading.studiolight_background_alpha(); + studiolight_path = shading.selected_studio_light().path(); } } -- cgit v1.2.3 From 534fcab9945c718f3b707f3afc1f7508f28e4cbb Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 26 May 2021 11:16:47 +0200 Subject: Fix T88552: Cycles changing Render Passes in viewport does not work --- intern/cycles/blender/blender_sync.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 64a2adccfe6..bacf57df48e 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -224,8 +224,18 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d if (b_v3d) { BlenderViewportParameters new_viewport_parameters(b_v3d); + if (viewport_parameters.modified(new_viewport_parameters)) { world_recalc = true; + has_updates_ = true; + } + + if (!has_updates_) { + Film *film = scene->film; + + const PassType new_display_pass = new_viewport_parameters.get_viewport_display_render_pass( + b_v3d); + has_updates_ |= film->get_display_pass() != new_display_pass; } } } -- cgit v1.2.3 From 2a09634d4158747511de1be998052a820b173e9f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 25 May 2021 19:10:34 +0200 Subject: Fix particlesystem not duplicating their pointcache in NO_MAIN case. Sharing data between duplicated IDs should be restricted to depsgraph (CoW) cases, not all NO_MAIN ones... While this was probably not an issue currently, we aim at using more and more out-of-main IDs for temp data processing. NOTE: Somewhat related to T88555, and similar issue as the one fixed in rBdfb963c70df5. --- 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 034af924ab1..e4cfe64df11 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2369,7 +2369,7 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f BLI_listbase_clear(&psysn->pathcachebufs); BLI_listbase_clear(&psysn->childcachebufs); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { /* XXX Disabled, fails when evaluating depsgraph after copying ID with no main for preview * creation. */ // BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0); -- cgit v1.2.3 From 12a06292af8678c2371b36369a96c088f438c9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 18 May 2021 13:09:29 +0200 Subject: Cycles: optimize attributes device updates When an `AttributeSet` is tagged as modified, which happens after the addition or removal of an `Attribute` from the set, during the following GeometryManager device update, we update and repack the kernel data for all attribute types. However, if we only add or remove a `float` attribute, `float2` or `float3` attributes should not be repacked for efficiency. This patch adds some mechanisms to detect which attribute types are modified from the AttributeSet. Firstly, this adds an `AttrKernelDataType` to map the data type of the Attribute to the one used in the kernel as there is no one to one match between the two since e.g. `Transform` or `float4` data are stored as `float3s` in the kernel. Then, this replaces the `AttributeSet.modified` boolean with a set of flags to detect which types have been modified. There is no specific flag type (e.g. `enum ModifiedType`), rather the flags used derive simply from the `AttrKernelDataType` enumeration, to keep things synchronized. The logic to remove an `Attribute` from the `AttributeSet` and tag the latter as modified is centralized in a new `AttributeSet.remove` method taking an iterator as input. Lastly, as some attributes like standard normals are not stored in the various kernel attribute arrays (`DeviceScene::attribute_*`), the modified flags are only set if the associated standard corresponds to an attribute which will be stored in the kernel's attribute arrays. This makes it so adding or removing such attributes does not trigger an unnecessary update of other type-related attributes. Reviewed By: brecht Differential Revision: https://developer.blender.org/D11373 --- intern/cycles/render/alembic.cpp | 3 +- intern/cycles/render/attribute.cpp | 61 ++++++++++++++++++++++----- intern/cycles/render/attribute.h | 33 ++++++++++++++- intern/cycles/render/geometry.cpp | 86 ++++++++++++++++++++++++-------------- 4 files changed, 138 insertions(+), 45 deletions(-) diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index cf345ee075d..dcb456dc1ce 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -654,8 +654,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, list::iterator it; for (it = attributes.attributes.begin(); it != attributes.attributes.end();) { if (cached_attributes.find(&(*it)) == cached_attributes.end()) { - attributes.attributes.erase(it++); - attributes.modified = true; + attributes.remove(it++); continue; } diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp index d6a638fd4cd..bf9d69cb47e 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/render/attribute.cpp @@ -383,6 +383,23 @@ AttributeStandard Attribute::name_standard(const char *name) return ATTR_STD_NONE; } +AttrKernelDataType Attribute::kernel_type(const Attribute &attr) +{ + if (attr.element == ATTR_ELEMENT_CORNER) { + return AttrKernelDataType::UCHAR4; + } + + if (attr.type == TypeDesc::TypeFloat) { + return AttrKernelDataType::FLOAT; + } + + if (attr.type == TypeFloat2) { + return AttrKernelDataType::FLOAT2; + } + + return AttrKernelDataType::FLOAT3; +} + void Attribute::get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set &tiles) const @@ -417,7 +434,7 @@ void Attribute::get_uv_tiles(Geometry *geom, /* Attribute Set */ AttributeSet::AttributeSet(Geometry *geometry, AttributePrimitive prim) - : geometry(geometry), prim(prim) + : modified_flag(~0u), geometry(geometry), prim(prim) { } @@ -440,7 +457,7 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme Attribute new_attr(name, type, element, geometry, prim); attributes.emplace_back(std::move(new_attr)); - modified = true; + tag_modified(attributes.back()); return &attributes.back(); } @@ -462,8 +479,7 @@ void AttributeSet::remove(ustring name) for (it = attributes.begin(); it != attributes.end(); it++) { if (&*it == attr) { - modified = true; - attributes.erase(it); + remove(it); return; } } @@ -608,8 +624,7 @@ void AttributeSet::remove(AttributeStandard std) for (it = attributes.begin(); it != attributes.end(); it++) { if (&*it == attr) { - modified = true; - attributes.erase(it); + remove(it); return; } } @@ -634,6 +649,12 @@ void AttributeSet::remove(Attribute *attribute) } } +void AttributeSet::remove(list::iterator it) +{ + tag_modified(*it); + attributes.erase(it); +} + void AttributeSet::resize(bool reserve_only) { foreach (Attribute &attr, attributes) { @@ -674,15 +695,13 @@ void AttributeSet::update(AttributeSet &&new_attributes) for (it = attributes.begin(); it != attributes.end();) { if (it->std != ATTR_STD_NONE) { if (new_attributes.find(it->std) == nullptr) { - modified = true; - attributes.erase(it++); + remove(it++); continue; } } else if (it->name != "") { if (new_attributes.find(it->name) == nullptr) { - modified = true; - attributes.erase(it++); + remove(it++); continue; } } @@ -699,7 +718,27 @@ void AttributeSet::clear_modified() foreach (Attribute &attr, attributes) { attr.modified = false; } - modified = false; + + modified_flag = 0; +} + +void AttributeSet::tag_modified(const Attribute &attr) +{ + /* Some attributes are not stored in the various kernel attribute arrays + * (DeviceScene::attribute_*), so the modified flags are only set if the associated standard + * corresponds to an attribute which will be stored in the kernel's attribute arrays. */ + const bool modifies_device_array = (attr.std != ATTR_STD_FACE_NORMAL && + attr.std != ATTR_STD_VERTEX_NORMAL); + + if (modifies_device_array) { + AttrKernelDataType kernel_type = Attribute::kernel_type(attr); + modified_flag |= (1u << kernel_type); + } +} + +bool AttributeSet::modified(AttrKernelDataType kernel_type) const +{ + return (modified_flag & (1u << kernel_type)) != 0; } /* AttributeRequest */ diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h index 18c9e5ab83a..004c267cabc 100644 --- a/intern/cycles/render/attribute.h +++ b/intern/cycles/render/attribute.h @@ -39,6 +39,21 @@ class Hair; class Mesh; struct Transform; +/* AttrKernelDataType. + * + * The data type of the device arrays storing the attribute's data. Those data types are different + * than the ones for attributes as some attribute types are stored in the same array, e.g. Point, + * Vector, and Transform are all stored as float3 in the kernel. + * + * The values of this enumeration are also used as flags to detect changes in AttributeSet. */ + +enum AttrKernelDataType { + FLOAT = 0, + FLOAT2 = 1, + FLOAT3 = 2, + UCHAR4 = 3, +}; + /* Attribute * * Arbitrary data layers on meshes. @@ -167,6 +182,8 @@ class Attribute { static const char *standard_name(AttributeStandard std); static AttributeStandard name_standard(const char *name); + static AttrKernelDataType kernel_type(const Attribute &attr); + void get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set &tiles) const; }; @@ -175,11 +192,12 @@ class Attribute { * Set of attributes on a mesh. */ class AttributeSet { + uint32_t modified_flag; + public: Geometry *geometry; AttributePrimitive prim; list attributes; - bool modified = true; AttributeSet(Geometry *geometry, AttributePrimitive prim); AttributeSet(AttributeSet &&) = default; @@ -197,6 +215,8 @@ class AttributeSet { void remove(Attribute *attribute); + void remove(list::iterator it); + void resize(bool reserve_only = false); void clear(bool preserve_voxel_data = false); @@ -204,7 +224,18 @@ class AttributeSet { * and remove any attribute not found on the new set from this. */ void update(AttributeSet &&new_attributes); + /* Return whether the attributes of the given kernel_type are modified, where "modified" means + * that some attributes of the given type were added or removed from this AttributeSet. This does + * not mean that the data of the remaining attributes in this AttributeSet were also modified. To + * check this, use Attribute.modified. */ + bool modified(AttrKernelDataType kernel_type) const; + void clear_modified(); + + private: + /* Set the relevant modified flag for the attribute. Only attributes that are stored in device + * arrays will be considered for tagging this AttributeSet as modified. */ + void tag_modified(const Attribute &attr); }; /* AttributeRequest diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 16fc36231b4..1c4b360750f 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -830,10 +830,13 @@ void GeometryManager::device_update_attributes(Device *device, dscene->attributes_float3.alloc(attr_float3_size); dscene->attributes_uchar4.alloc(attr_uchar4_size); - const bool copy_all_data = dscene->attributes_float.need_realloc() || - dscene->attributes_float2.need_realloc() || - dscene->attributes_float3.need_realloc() || - dscene->attributes_uchar4.need_realloc(); + /* The order of those flags needs to match that of AttrKernelDataType. */ + const bool attributes_need_realloc[4] = { + dscene->attributes_float.need_realloc(), + dscene->attributes_float2.need_realloc(), + dscene->attributes_float3.need_realloc(), + dscene->attributes_uchar4.need_realloc(), + }; size_t attr_float_offset = 0; size_t attr_float2_offset = 0; @@ -852,7 +855,7 @@ void GeometryManager::device_update_attributes(Device *device, if (attr) { /* force a copy if we need to reallocate all the data */ - attr->modified |= copy_all_data; + attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; } update_attribute_element_offset(geom, @@ -875,7 +878,7 @@ void GeometryManager::device_update_attributes(Device *device, if (subd_attr) { /* force a copy if we need to reallocate all the data */ - subd_attr->modified |= copy_all_data; + subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)]; } update_attribute_element_offset(mesh, @@ -906,6 +909,10 @@ void GeometryManager::device_update_attributes(Device *device, foreach (AttributeRequest &req, attributes.requests) { Attribute *attr = values.find(req); + if (attr) { + attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; + } + update_attribute_element_offset(object->geometry, dscene->attributes_float, attr_float_offset, @@ -941,10 +948,10 @@ void GeometryManager::device_update_attributes(Device *device, /* copy to device */ progress.set_status("Updating Mesh", "Copying Attributes to device"); - dscene->attributes_float.copy_to_device(); - dscene->attributes_float2.copy_to_device(); - dscene->attributes_float3.copy_to_device(); - dscene->attributes_uchar4.copy_to_device(); + dscene->attributes_float.copy_to_device_if_modified(); + dscene->attributes_float2.copy_to_device_if_modified(); + dscene->attributes_float3.copy_to_device_if_modified(); + dscene->attributes_uchar4.copy_to_device_if_modified(); if (progress.get_cancel()) return; @@ -1431,24 +1438,46 @@ static void update_device_flags_attribute(uint32_t &device_update_flags, continue; } - if (attr.element == ATTR_ELEMENT_CORNER) { - device_update_flags |= ATTR_UCHAR4_MODIFIED; - } - else if (attr.type == TypeDesc::TypeFloat) { - device_update_flags |= ATTR_FLOAT_MODIFIED; - } - else if (attr.type == TypeFloat2) { - device_update_flags |= ATTR_FLOAT2_MODIFIED; - } - else if (attr.type == TypeDesc::TypeMatrix) { - device_update_flags |= ATTR_FLOAT3_MODIFIED; - } - else if (attr.element != ATTR_ELEMENT_VOXEL) { - device_update_flags |= ATTR_FLOAT3_MODIFIED; + AttrKernelDataType kernel_type = Attribute::kernel_type(attr); + + switch (kernel_type) { + case AttrKernelDataType::FLOAT: { + device_update_flags |= ATTR_FLOAT_MODIFIED; + break; + } + case AttrKernelDataType::FLOAT2: { + device_update_flags |= ATTR_FLOAT2_MODIFIED; + break; + } + case AttrKernelDataType::FLOAT3: { + device_update_flags |= ATTR_FLOAT3_MODIFIED; + break; + } + case AttrKernelDataType::UCHAR4: { + device_update_flags |= ATTR_UCHAR4_MODIFIED; + break; + } } } } +static void update_attribute_realloc_flags(uint32_t &device_update_flags, + const AttributeSet &attributes) +{ + if (attributes.modified(AttrKernelDataType::FLOAT)) { + device_update_flags |= ATTR_FLOAT_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::FLOAT2)) { + device_update_flags |= ATTR_FLOAT2_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::FLOAT3)) { + device_update_flags |= ATTR_FLOAT3_NEEDS_REALLOC; + } + if (attributes.modified(AttrKernelDataType::UCHAR4)) { + device_update_flags |= ATTR_UCHAR4_NEEDS_REALLOC; + } +} + void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress) { if (!need_update() && !need_flags_update) { @@ -1471,16 +1500,11 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro foreach (Geometry *geom, scene->geometry) { geom->has_volume = false; - if (geom->attributes.modified) { - device_update_flags |= ATTRS_NEED_REALLOC; - } + update_attribute_realloc_flags(device_update_flags, geom->attributes); if (geom->is_mesh()) { Mesh *mesh = static_cast(geom); - - if (mesh->subd_attributes.modified) { - device_update_flags |= ATTRS_NEED_REALLOC; - } + update_attribute_realloc_flags(device_update_flags, mesh->subd_attributes); } foreach (Node *node, geom->get_used_shaders()) { -- cgit v1.2.3 From afec66c024dc2b75447537d45406c06342ec201e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 26 May 2021 12:24:56 +0200 Subject: Fix T88588: crash when muting node with multi input socket The bug existed before the new evaluator already, but the new evaluator is more sensitive to this kind of error. --- source/blender/nodes/intern/node_tree_ref.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 5c0bc0b5ebc..8699736e543 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -297,6 +297,12 @@ void OutputSocketRef::foreach_logical_target( skipped_fn.call_safe(target); for (const InternalLinkRef *internal_link : target_node.internal_links()) { if (&internal_link->from() == &target) { + /* The internal link only forwards the first incoming link. */ + if (target.is_multi_input_socket()) { + if (target.directly_linked_links()[0] != link) { + continue; + } + } const OutputSocketRef &mute_output = internal_link->to(); skipped_fn.call_safe(target); skipped_fn.call_safe(mute_output); -- cgit v1.2.3 From b67423f80663990f972f4317d38b8e7662b9e8eb Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 26 May 2021 14:19:01 +0200 Subject: Nodes: fix threading issues with node ui storage Calling BKE_nodetree_attribute_hint_add from multiple threads still was not safe before.. One issue was that context_map embedded its values directly. So when context_map grows, all NodeUIStorage would move as well. I could patch around that by using std::unique_ptr in a few places, but that just becomes too complex for now. Instead I simplified the locking a bit by adding just locking a mutex in NodeTreeUIStorage all the time while an attribute hint is added. Differential Revision: https://developer.blender.org/D11399 --- source/blender/blenkernel/BKE_node_ui_storage.hh | 10 +------- .../blender/blenkernel/intern/node_ui_storage.cc | 27 +++++++++++----------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh index 8bf89cd8f58..4ec165aad8c 100644 --- a/source/blender/blenkernel/BKE_node_ui_storage.hh +++ b/source/blender/blenkernel/BKE_node_ui_storage.hh @@ -95,21 +95,13 @@ struct AvailableAttributeInfo { }; struct NodeUIStorage { - std::mutex mutex; blender::Vector warnings; blender::Set attribute_hints; - - NodeUIStorage() = default; - /* Needed because the mutex can't be moved or copied. */ - NodeUIStorage(NodeUIStorage &&other) - : warnings(std::move(other.warnings)), attribute_hints(std::move(other.attribute_hints)) - { - } }; struct NodeTreeUIStorage { + std::mutex mutex; blender::Map> context_map; - std::mutex context_map_mutex; /** * Attribute search uses this to store the fake info for the string typed into a node, in order diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc index 7a28fd295fb..e5e9f00c7c3 100644 --- a/source/blender/blenkernel/intern/node_ui_storage.cc +++ b/source/blender/blenkernel/intern/node_ui_storage.cc @@ -39,7 +39,7 @@ using blender::Vector; * bNodeTree struct in DNA. This could change if the node tree had a runtime struct. */ static std::mutex global_ui_storage_mutex; -static void ui_storage_ensure(bNodeTree &ntree) +static NodeTreeUIStorage &ui_storage_ensure(bNodeTree &ntree) { /* As an optimization, only acquire a lock if the UI storage doesn't exist, * because it only needs to be allocated once for every node tree. */ @@ -50,6 +50,7 @@ static void ui_storage_ensure(bNodeTree &ntree) ntree.ui_storage = new NodeTreeUIStorage(); } } + return *ntree.ui_storage; } const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C, @@ -90,7 +91,7 @@ void BKE_nodetree_ui_storage_free_for_context(bNodeTree &ntree, { NodeTreeUIStorage *ui_storage = ntree.ui_storage; if (ui_storage != nullptr) { - std::lock_guard lock(ui_storage->context_map_mutex); + std::lock_guard lock(ui_storage->mutex); ui_storage->context_map.remove(context); } } @@ -126,20 +127,14 @@ static void node_error_message_log(bNodeTree &ntree, } } -static NodeUIStorage &node_ui_storage_ensure(bNodeTree &ntree, +static NodeUIStorage &node_ui_storage_ensure(NodeTreeUIStorage &locked_ui_storage, const NodeTreeEvaluationContext &context, const bNode &node) { - ui_storage_ensure(ntree); - NodeTreeUIStorage &ui_storage = *ntree.ui_storage; - - std::lock_guard lock(ui_storage.context_map_mutex); Map &node_tree_ui_storage = - ui_storage.context_map.lookup_or_add_default(context); - + locked_ui_storage.context_map.lookup_or_add_default(context); NodeUIStorage &node_ui_storage = node_tree_ui_storage.lookup_or_add_default_as( StringRef(node.name)); - return node_ui_storage; } @@ -149,10 +144,12 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree, const NodeWarningType type, std::string message) { + NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree); + std::lock_guard lock{ui_storage.mutex}; + node_error_message_log(ntree, node, message, type); - NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node); - std::lock_guard lock{node_ui_storage.mutex}; + NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node); node_ui_storage.warnings.append({type, std::move(message)}); } @@ -163,8 +160,10 @@ void BKE_nodetree_attribute_hint_add(bNodeTree &ntree, const AttributeDomain domain, const CustomDataType data_type) { - NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node); - std::lock_guard lock{node_ui_storage.mutex}; + NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree); + std::lock_guard lock{ui_storage.mutex}; + + NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node); node_ui_storage.attribute_hints.add_as( AvailableAttributeInfo{attribute_name, domain, data_type}); } -- cgit v1.2.3 From 06f86dd4d9a2a4250279f40acb6ca48191a094da Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 26 May 2021 15:41:38 +0200 Subject: GPencil: Bake GPencil object transforms into a new GPencil object This operator is a common request of animators to convert the transformation (inluding modifiers) of one grease pencil object, into a new object, generating strokes. Reviewed By: pepeland Maniphest Tasks: T87424 Differential Revision: https://developer.blender.org/D11014 --- release/scripts/startup/bl_ui/space_view3d.py | 1 + source/blender/editors/gpencil/CMakeLists.txt | 1 + .../editors/gpencil/gpencil_bake_animation.c | 423 +++++++++++++++++++++ source/blender/editors/gpencil/gpencil_intern.h | 22 ++ source/blender/editors/gpencil/gpencil_mesh.c | 19 - source/blender/editors/gpencil/gpencil_ops.c | 1 + 6 files changed, 448 insertions(+), 19 deletions(-) create mode 100644 source/blender/editors/gpencil/gpencil_bake_animation.c diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 18c6564b7d4..ae18fc4fb76 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2314,6 +2314,7 @@ class VIEW3D_MT_object_animation(Menu): layout.operator("nla.bake", text="Bake Action...") layout.operator("gpencil.bake_mesh_animation", text="Bake Mesh to Grease Pencil...") + layout.operator("gpencil.bake_grease_pencil_animation", text="Bake Object Transform to Grease Pencil...") class VIEW3D_MT_object_rigid_body(Menu): diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index f7ab72a8a43..bff7310e9f7 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC gpencil_add_monkey.c gpencil_add_stroke.c gpencil_armature.c + gpencil_bake_animation.c gpencil_convert.c gpencil_data.c gpencil_edit.c diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c new file mode 100644 index 00000000000..6063594826e --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_bake_animation.c @@ -0,0 +1,423 @@ +/* + * 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 edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" + +#include "DNA_anim_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_anim_data.h" +#include "BKE_context.h" +#include "BKE_duplilist.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_gpencil.h" +#include "ED_transform_snap_object_context.h" + +#include "gpencil_intern.h" + +/* Check frame_end is always > start frame! */ +static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), + struct Scene *UNUSED(scene), + struct PointerRNA *ptr) +{ + int frame_start = RNA_int_get(ptr, "frame_start"); + int frame_end = RNA_int_get(ptr, "frame_end"); + + if (frame_end <= frame_start) { + RNA_int_set(ptr, "frame_end", frame_start + 1); + } +} + +/* Extract mesh animation to Grease Pencil. */ +static bool gpencil_bake_grease_pencil_animation_poll(bContext *C) +{ + Object *obact = CTX_data_active_object(C); + if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) { + return false; + } + + /* Check if grease pencil or empty for dupli groups. */ + if ((obact == NULL) || ((obact->type != OB_GPENCIL) && (obact->type != OB_EMPTY))) { + return false; + } + + /* Only if the current view is 3D View. */ + ScrArea *area = CTX_wm_area(C); + return (area && area->spacetype); +} + +typedef struct GpBakeOb { + struct GpBakeOb *next, *prev; + Object *ob; +} GpBakeOb; + +/* Get list of keyframes used by selected objects. */ +static void animdata_keyframe_list_get(ListBase *ob_list, + const bool only_selected, + GHash *r_keyframes) +{ + /* Loop all objects to get the list of keyframes used. */ + LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) { + Object *ob = elem->ob; + AnimData *adt = BKE_animdata_from_id(&ob->id); + if ((adt == NULL) || (adt->action == NULL)) { + continue; + } + LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) { + int i; + BezTriple *bezt; + for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) { + /* Keyframe number is x value of point. */ + if ((bezt->f2 & SELECT) || (!only_selected)) { + /* Insert only one key for each keyframe number. */ + int key = (int)bezt->vec[1][0]; + if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) { + BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + } + } + } +} + +static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list) +{ + GpBakeOb *elem = NULL; + ListBase *lb; + DupliObject *dob; + lb = object_duplilist(depsgraph, scene, ob); + for (dob = lb->first; dob; dob = dob->next) { + if (dob->ob->type != OB_GPENCIL) { + continue; + } + + elem = MEM_callocN(sizeof(GpBakeOb), __func__); + elem->ob = dob->ob; + BLI_addtail(list, elem); + } + + free_object_duplilist(lb); +} + +static void gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list) +{ + GpBakeOb *elem = NULL; + + /* Add active object. In some files this could not be in selected array. */ + Object *obact = CTX_data_active_object(C); + + if (obact->type == OB_GPENCIL) { + elem = MEM_callocN(sizeof(GpBakeOb), __func__); + elem->ob = obact; + BLI_addtail(list, elem); + } + /* Add duplilist. */ + else if (obact->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, obact, list); + } + + /* Add other selected objects. */ + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob == obact) { + continue; + } + /* Add selected objects.*/ + if (ob->type == OB_GPENCIL) { + elem = MEM_callocN(sizeof(GpBakeOb), __func__); + elem->ob = ob; + BLI_addtail(list, elem); + } + + /* Add duplilist. */ + if (ob->type == OB_EMPTY) { + gpencil_bake_duplilist(depsgraph, scene, ob, list); + } + } + CTX_DATA_END; +} + +static void gpencil_bake_free_ob_list(ListBase *list) +{ + LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) { + MEM_SAFE_FREE(elem); + } +} + +static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + + ListBase ob_selected_list = {NULL, NULL}; + gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list); + + /* Grab all relevant settings. */ + const int step = RNA_int_get(op->ptr, "step"); + + const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ? + scene->r.sfra : + RNA_int_get(op->ptr, "frame_start"); + + const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ? + scene->r.efra : + RNA_int_get(op->ptr, "frame_end"); + + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected"); + const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start; + const int project_type = RNA_enum_get(op->ptr, "project_type"); + + /* Create a new grease pencil object. */ + Object *ob_gpencil = NULL; + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + ob_gpencil = ED_gpencil_add_object(C, scene->cursor.location, local_view_bits); + float invmat[4][4]; + invert_m4_m4(invmat, ob_gpencil->obmat); + + bGPdata *gpd_dst = (bGPdata *)ob_gpencil->data; + gpd_dst->draw_mode = GP_DRAWMODE_2D; + + /* Set cursor to indicate working. */ + WM_cursor_wait(true); + + GP_SpaceConversion gsc = {NULL}; + SnapObjectContext *sctx = NULL; + if (project_type != GP_REPROJECT_KEEP) { + /* Init space conversion stuff. */ + gpencil_point_conversion_init(C, &gsc); + /* Move the grease pencil object to conversion data. */ + gsc.ob = ob_gpencil; + + /* Init snap context for geometry projection. */ + sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); + } + + /* Loop all frame range. */ + int oldframe = (int)DEG_get_ctime(depsgraph); + int key = -1; + + /* Get list of keyframes. */ + GHash *keyframe_list = BLI_ghash_int_new(__func__); + if (only_selected) { + animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list); + } + + for (int i = frame_start; i < frame_end + 1; i++) { + key++; + /* Jump if not step limit but include last frame always. */ + if ((key % step != 0) && (i != frame_end)) { + continue; + } + + /* Check if frame is in the list of frames to be exported. */ + if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) { + continue; + } + + /* Move scene to new frame. */ + CFRA = i; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Loop all objects in the list. */ + LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) { + Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob); + bGPdata *gpd_src = ob_eval->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) { + /* Create destination layer. */ + char *layer_name; + layer_name = BLI_sprintfN("%s_%s", elem->ob->id.name + 2, gpl_src->info); + bGPDlayer *gpl_dst = BKE_gpencil_layer_named_get(gpd_dst, layer_name); + if (gpl_dst == NULL) { + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, layer_name, true, false); + } + MEM_freeN(layer_name); + + /* Layer Transform matrix. */ + float matrix[4][4]; + BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix); + + /* Duplicate frame. */ + bGPDframe *gpf_src = BKE_gpencil_layer_frame_get(gpl_src, CFRA, GP_GETFRAME_USE_PREV); + if (gpf_src == NULL) { + continue; + } + bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true); + gpf_dst->framenum = CFRA + frame_offset; + gpf_dst->flag &= ~GP_FRAME_SELECT; + BLI_addtail(&gpl_dst->frames, gpf_dst); + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_dst->strokes) { + /* Create material of the stroke. */ + Material *ma_src = BKE_object_material_get(elem->ob, gps->mat_nr + 1); + bool found = false; + for (int index = 0; index < ob_gpencil->totcol; index++) { + Material *ma_dst = BKE_object_material_get(ob_gpencil, index + 1); + if (ma_src == ma_dst) { + found = true; + break; + } + } + if (!found) { + BKE_object_material_slot_add(bmain, ob_gpencil); + BKE_object_material_assign( + bmain, ob_gpencil, ma_src, ob_gpencil->totcol, BKE_MAT_ASSIGN_USERPREF); + } + + /* Set new material index. */ + gps->mat_nr = BKE_gpencil_object_material_index_get(ob_gpencil, ma_src); + + /* Update point location to new object space. */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + mul_m4_v3(matrix, &pt->x); + mul_m4_v3(invmat, &pt->x); + } + + /* Reproject stroke. */ + if (project_type != GP_REPROJECT_KEEP) { + ED_gpencil_stroke_reproject( + depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false); + } + else { + BKE_gpencil_stroke_geometry_update(gpd_dst, gps); + } + } + } + } + } + /* Return scene frame state and DB to original state. */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph); + + /* Free memory. */ + gpencil_bake_free_ob_list(&ob_selected_list); + if (sctx != NULL) { + ED_transform_snap_object_context_destroy(sctx); + } + /* Free temp hash table. */ + if (keyframe_list != NULL) { + BLI_ghash_free(keyframe_list, NULL, NULL); + } + + /* Notifiers. */ + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + DEG_id_tag_update(&gpd_dst->id, ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + /* Reset cursor. */ + WM_cursor_wait(false); + + /* done */ + return OPERATOR_FINISHED; +} + +static int gpencil_bake_grease_pencil_animation_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + + prop = RNA_struct_find_property(op->ptr, "frame_start"); + if (!RNA_property_is_set(op->ptr, prop)) { + const int frame_start = RNA_property_int_get(op->ptr, prop); + if (frame_start < scene->r.sfra) { + RNA_property_int_set(op->ptr, prop, scene->r.sfra); + } + } + + prop = RNA_struct_find_property(op->ptr, "frame_end"); + if (!RNA_property_is_set(op->ptr, prop)) { + const int frame_end = RNA_property_int_get(op->ptr, prop); + if (frame_end > scene->r.efra) { + RNA_property_int_set(op->ptr, prop, scene->r.efra); + } + } + + /* Show popup dialog to allow editing. */ + return WM_operator_props_dialog_popup(C, op, 250); +} + +void GPENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Bake Object Transform to Grease Pencil"; + ot->idname = "GPENCIL_OT_bake_grease_pencil_animation"; + ot->description = "Bake grease pencil object transform to grease pencil keyframes"; + + /* callbacks */ + ot->invoke = gpencil_bake_grease_pencil_animation_invoke; + ot->exec = gpencil_bake_grease_pencil_animation_exec; + ot->poll = gpencil_bake_grease_pencil_animation_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_int( + ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000); + + prop = RNA_def_int( + ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000); + RNA_def_property_update_runtime(prop, gpencil_bake_set_frame_end); + + prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100); + + RNA_def_boolean( + ot->srna, "only_selected", 0, "Only Selected Keyframes", "Convert only selected keyframes"); + RNA_def_int( + ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); + + RNA_def_enum(ot->srna, "project_type", reproject_type, GP_REPROJECT_KEEP, "Projection Type", ""); +} diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 9f48c81c8f1..665401e8fee 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -444,6 +444,7 @@ void GPENCIL_OT_frame_clean_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot); +void GPENCIL_OT_bake_grease_pencil_animation(struct wmOperatorType *ot); void GPENCIL_OT_image_to_grease_pencil(struct wmOperatorType *ot); void GPENCIL_OT_trace_image(struct wmOperatorType *ot); @@ -750,4 +751,25 @@ struct GP_EditableStrokes_Iter { } \ (void)0 +/* Reused items for bake operators. */ +static const EnumPropertyItem reproject_type[] = { + {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, + {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, + {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, + {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, + {GP_REPROJECT_VIEW, + "VIEW", + 0, + "View", + "Reproject the strokes to end up on the same plane, as if drawn from the current " + "viewpoint " + "using 'Cursor' Stroke Placement"}, + {GP_REPROJECT_CURSOR, + "CURSOR", + 0, + "Cursor", + "Reproject the strokes using the orientation of 3D cursor"}, + {0, NULL, 0, NULL, NULL}, +}; + /* ****************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index b7ed77801c0..e85221fba8c 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -402,25 +402,6 @@ static int gpencil_bake_mesh_animation_invoke(bContext *C, void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot) { - static const EnumPropertyItem reproject_type[] = { - {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, - {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, - {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, - {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, - {GP_REPROJECT_VIEW, - "VIEW", - 0, - "View", - "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " - "using 'Cursor' Stroke Placement"}, - {GP_REPROJECT_CURSOR, - "CURSOR", - 0, - "Cursor", - "Reproject the strokes using the orientation of 3D cursor"}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem target_object_modes[] = { {GP_TARGET_OB_NEW, "NEW", 0, "New Object", ""}, {GP_TARGET_OB_SELECTED, "SELECTED", 0, "Selected Object", ""}, diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 698d784c3b0..0e9ce1d603f 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -622,6 +622,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_convert); WM_operatortype_append(GPENCIL_OT_bake_mesh_animation); + WM_operatortype_append(GPENCIL_OT_bake_grease_pencil_animation); WM_operatortype_append(GPENCIL_OT_image_to_grease_pencil); #ifdef WITH_POTRACE -- cgit v1.2.3 From ba5b4d1bd68e8b9a12279d8be9e6d84f496bd44b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 26 May 2021 16:06:01 +0200 Subject: Fix T88250: crash when instancing object in disabled collection This issue was that `BKE_object_eval_uber_data` was not called for the text object, because its geometry was not dependent upon and its `is_directly_visible` tag was `false`. The crash happens in rendering code, because the evaluated data is missing. This not only affects text objects, but all object types that have a geometry component that geometry nodes does not support yet. The solution is to just add the missing dependencies. Differential Revision: https://developer.blender.org/D11385 --- source/blender/depsgraph/DEG_depsgraph_build.h | 2 ++ source/blender/depsgraph/intern/depsgraph_build.cc | 6 ++++++ source/blender/modifiers/intern/MOD_nodes.cc | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 5f9e78837a7..b4acf9b010c 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -189,6 +189,8 @@ void DEG_add_customdata_mask(struct DepsNodeHandle *handle, struct ID *DEG_get_id_from_handle(struct DepsNodeHandle *node_handle); struct Depsgraph *DEG_get_graph_from_handle(struct DepsNodeHandle *node_handle); +bool DEG_object_has_geometry_component(struct Object *object); + /* ************************************************ */ #ifdef __cplusplus diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index 6c1e91d068b..9e9191c5ab9 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -62,6 +62,7 @@ #include "intern/depsgraph_registry.h" #include "intern/depsgraph_relation.h" +#include "intern/depsgraph_tag.h" #include "intern/depsgraph_type.h" /* ****************** */ @@ -109,6 +110,11 @@ void DEG_add_object_relation(DepsNodeHandle *node_handle, deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description); } +bool DEG_object_has_geometry_component(Object *object) +{ + return deg::geometry_tag_to_component(&object->id) != deg::NodeType::UNDEFINED; +} + void DEG_add_collection_geometry_relation(DepsNodeHandle *node_handle, Collection *collection, const char *description) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index d808052e5f5..fd3634ad278 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -185,7 +185,7 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec if (object.type == OB_EMPTY && object.instance_collection != nullptr) { add_collection_relation(ctx, *object.instance_collection); } - else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) { + else if (DEG_object_has_geometry_component(&object)) { DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier"); DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask); } -- cgit v1.2.3 From d5a5575685b650d297b40ace925042ce1525ecae Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 26 May 2021 16:07:03 +0200 Subject: Fix: GPencil mask shows in view layer render Currently when rendering the view layer of a grease pencil layer that has a mask layer attached, the mask layer would show in the rendered image. This is inconsistent with the default behaviour with no mask on the grease pencil layer, because it would only render what's on that particular layer and not anything from any other layer. This patch makes the masks invisible in the render. Note: This might seem like not the best solution, but because masks are just regular grease pencil layers, it's tricky to pass this edge-case to the drawing code. The way it is handled right now is the best I could come up with, without making changes that could affect something else. Reviewed By: antoniov Maniphest Tasks: T88202 Differential Revision: https://developer.blender.org/D11403 --- source/blender/blenkernel/intern/gpencil.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 9c84d155330..182d06f8f72 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -2659,6 +2659,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, bGPDframe *act_gpf = gpl->actframe; bGPDframe *sta_gpf = act_gpf; bGPDframe *end_gpf = act_gpf ? act_gpf->next : NULL; + float prev_opacity = gpl->opacity; if (gpl->flag & GP_LAYER_HIDE) { continue; @@ -2674,9 +2675,12 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, * This is used only in final render and never in Viewport. */ if ((view_layer != NULL) && (gpl->viewlayername[0] != '\0') && (!STREQ(view_layer->name, gpl->viewlayername))) { - /* If the layer is used as mask, cannot be filtered or the masking system - * will crash because needs the mask layer in the draw pipeline. */ - if (!gpencil_is_layer_mask(view_layer, gpd, gpl)) { + /* Do not skip masks when rendering the viewlayer so that it can still be used to clip + * other layers. Instead set their opacity to zero. */ + if (gpencil_is_layer_mask(view_layer, gpd, gpl)) { + gpl->opacity = 0.0f; + } + else { continue; } } @@ -2771,6 +2775,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, if (layer_cb) { layer_cb(gpl, act_gpf, NULL, thunk); } + gpl->opacity = prev_opacity; continue; } @@ -2808,6 +2813,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, /* If layer solo mode and Paint mode, only keyframes with data are displayed. */ if (GPENCIL_PAINT_MODE(gpd) && (gpl->flag & GP_LAYER_SOLO_MODE) && (act_gpf->framenum != cfra)) { + gpl->opacity = prev_opacity; continue; } @@ -2818,6 +2824,9 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, stroke_cb(gpl, act_gpf, gps, thunk); } } + + /* Restore the opacity in case it was overwritten (used to hide masks in render). */ + gpl->opacity = prev_opacity; } } -- cgit v1.2.3 From e459a25e6cbe9321ad25f87843e2fe5a8a2306f9 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 26 May 2021 16:46:00 +0200 Subject: GPencil: Add option to disable masks in view layer This patch adds an option in the Layers > Relations panel called "Disable Masks in Render". When checked, no masks on this layer are included in the render. Example: | {F10087680} | {F10087681} | See T88202 for why this is needed. Reviewed By: antoniov Maniphest Tasks: T88202 Differential Revision: https://developer.blender.org/D11234 --- release/scripts/startup/bl_ui/properties_grease_pencil_common.py | 6 +++++- source/blender/blenkernel/intern/gpencil.c | 5 +++++ source/blender/draw/engines/gpencil/gpencil_cache_utils.c | 8 +++++++- source/blender/makesdna/DNA_gpencil_types.h | 2 ++ source/blender/makesrna/intern/rna_gpencil.c | 6 ++++++ 5 files changed, 25 insertions(+), 2 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 55a49878b71..0111bdea8b1 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -856,7 +856,11 @@ class GreasePencilLayerRelationsPanel: col = layout.row(align=True) col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer") - + + col = layout.row(align=True) + # Only enable this property when a view layer is selected. + col.enabled = bool(gpl.viewlayer_render) + col.prop(gpl, "disable_masks_viewlayer") class GreasePencilLayerDisplayPanel: diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index ea3782c274a..409b9fb0a2c 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -2624,6 +2624,11 @@ static bool gpencil_is_layer_mask(ViewLayer *view_layer, bGPdata *gpd, bGPDlayer continue; } + /* Skip if masks are disabled for this view layer. */ + if (gpl->flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER) { + continue; + } + LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) { if (STREQ(gpl_mask->info, mask->name)) { return true; diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index adb70f97585..af8b029a08e 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -274,7 +274,13 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, const bool override_vertcol = (pd->v3d_color_type != -1); const bool is_vert_col_mode = (pd->v3d_color_type == V3D_SHADING_VERTEX_COLOR) || GPENCIL_VERTEX_MODE(gpd) || pd->is_render; - bool is_masked = (gpl->flag & GP_LAYER_USE_MASK) && !BLI_listbase_is_empty(&gpl->mask_layers); + const bool is_viewlayer_render = pd->is_render && (gpl->viewlayername[0] != '\0') && + STREQ(pd->view_layer->name, gpl->viewlayername); + const bool disable_masks_render = is_viewlayer_render && + (gpl->flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER); + bool is_masked = disable_masks_render ? false : + (gpl->flag & GP_LAYER_USE_MASK) && + !BLI_listbase_is_empty(&gpl->mask_layers); float vert_col_opacity = (override_vertcol) ? (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) : diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 0acf979516e..ea3c1ff7275 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -560,6 +560,8 @@ typedef enum eGPDlayer_Flag { GP_LAYER_USE_MASK = (1 << 13), /*TODO: DEPRECATED */ /* Ruler Layer */ GP_LAYER_IS_RULER = (1 << 14), + /* Disable masks in viewlayer render */ + GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER = (1 << 15), } eGPDlayer_Flag; /** #bGPDlayer.onion_flag */ diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 91e13a4bee3..01f564cb272 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -2114,6 +2114,12 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) "ViewLayer", "Only include Layer in this View Layer render output (leave blank to include always)"); + prop = RNA_def_property(srna, "disable_masks_viewlayer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER); + RNA_def_property_ui_text( + prop, "Disable Masks in Render", "Exclude the mask layers when rendering the viewlayer"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* blend mode */ prop = RNA_def_property(srna, "blend_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "blend_mode"); -- cgit v1.2.3 From 87055dc71b0d50cd25660969b55cda7d44af6a12 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 26 May 2021 16:49:17 +0200 Subject: GPU: Compute Pipeline. With the compute pipeline calculation can be offloaded to the GPU. This patch only adds the framework for compute. So no changes for users at this moment. NOTE: As this is an OpenGL4.3 feature it must always have a fallback. Use `GPU_compute_shader_support` to check if compute pipeline can be used. Check `gpu_shader_compute*` test cases for usage. This patch also adds support for shader storage buffer objects and device only vertex/index buffers. An alternative that had been discussed was adding this to the `GPUBatch`, this was eventually not chosen as it would lead to more code when used as part of a shading group. The idea is that we add an `eDRWCommandType` in the near future. Reviewed By: fclem Differential Revision: https://developer.blender.org/D10913 --- source/blender/draw/intern/draw_manager_shader.c | 1 + source/blender/gpu/CMakeLists.txt | 5 + source/blender/gpu/GPU_capabilities.h | 2 + source/blender/gpu/GPU_compute.h | 38 +++ source/blender/gpu/GPU_index_buffer.h | 12 + source/blender/gpu/GPU_shader.h | 7 + source/blender/gpu/GPU_state.h | 1 + source/blender/gpu/GPU_vertex_buffer.h | 10 + source/blender/gpu/intern/gpu_backend.hh | 1 + source/blender/gpu/intern/gpu_capabilities.cc | 10 + .../blender/gpu/intern/gpu_capabilities_private.hh | 2 + source/blender/gpu/intern/gpu_compute.cc | 41 +++ source/blender/gpu/intern/gpu_index_buffer.cc | 42 +++ .../blender/gpu/intern/gpu_index_buffer_private.hh | 12 +- source/blender/gpu/intern/gpu_shader.cc | 59 +++- source/blender/gpu/intern/gpu_shader_interface.cc | 9 + source/blender/gpu/intern/gpu_shader_interface.hh | 6 + source/blender/gpu/intern/gpu_shader_private.hh | 1 + source/blender/gpu/intern/gpu_vertex_buffer.cc | 15 + .../gpu/intern/gpu_vertex_buffer_private.hh | 3 + source/blender/gpu/opengl/gl_backend.cc | 2 + source/blender/gpu/opengl/gl_backend.hh | 7 + source/blender/gpu/opengl/gl_compute.cc | 35 +++ source/blender/gpu/opengl/gl_compute.hh | 30 ++ source/blender/gpu/opengl/gl_index_buffer.cc | 34 ++- source/blender/gpu/opengl/gl_index_buffer.hh | 7 + source/blender/gpu/opengl/gl_shader.cc | 38 ++- source/blender/gpu/opengl/gl_shader.hh | 4 +- source/blender/gpu/opengl/gl_shader_interface.cc | 45 ++- source/blender/gpu/opengl/gl_state.hh | 3 + source/blender/gpu/opengl/gl_texture.cc | 2 +- source/blender/gpu/opengl/gl_vertex_buffer.cc | 45 ++- source/blender/gpu/opengl/gl_vertex_buffer.hh | 8 + source/blender/gpu/tests/gpu_shader_test.cc | 301 +++++++++++++++++++++ 34 files changed, 819 insertions(+), 19 deletions(-) create mode 100644 source/blender/gpu/GPU_compute.h create mode 100644 source/blender/gpu/intern/gpu_compute.cc create mode 100644 source/blender/gpu/opengl/gl_compute.cc create mode 100644 source/blender/gpu/opengl/gl_compute.hh create mode 100644 source/blender/gpu/tests/gpu_shader_test.cc diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 2aad1f10154..83d0030f89b 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -396,6 +396,7 @@ GPUShader *DRW_shader_create_with_transform_feedback(const char *vert, datatoc_gpu_shader_depth_only_frag_glsl, geom, NULL, + NULL, defines, prim_type, varying_names, diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index f1ffd7827b8..cf6009c2881 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -62,6 +62,7 @@ set(SRC intern/gpu_buffers.c intern/gpu_capabilities.cc intern/gpu_codegen.c + intern/gpu_compute.cc intern/gpu_context.cc intern/gpu_debug.cc intern/gpu_drawlist.cc @@ -91,6 +92,7 @@ set(SRC opengl/gl_backend.cc opengl/gl_batch.cc + opengl/gl_compute.cc opengl/gl_context.cc opengl/gl_debug.cc opengl/gl_debug_layer.cc @@ -113,6 +115,7 @@ set(SRC GPU_buffers.h GPU_capabilities.h GPU_common.h + GPU_compute.h GPU_context.h GPU_debug.h GPU_drawlist.h @@ -163,6 +166,7 @@ set(SRC opengl/gl_backend.hh opengl/gl_batch.hh + opengl/gl_compute.hh opengl/gl_context.hh opengl/gl_debug.hh opengl/gl_drawlist.hh @@ -390,6 +394,7 @@ if(WITH_GTESTS) if(WITH_OPENGL_DRAW_TESTS) set(TEST_SRC tests/gpu_testing.cc + tests/gpu_shader_test.cc tests/gpu_testing.hh ) diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index f54ecece659..45c656b49be 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -55,6 +55,8 @@ bool GPU_use_main_context_workaround(void); bool GPU_use_hq_normals_workaround(void); bool GPU_crappy_amd_driver(void); +bool GPU_compute_shader_support(void); +bool GPU_shader_storage_buffer_objects_support(void); bool GPU_shader_image_load_store_support(void); bool GPU_mem_stats_supported(void); diff --git a/source/blender/gpu/GPU_compute.h b/source/blender/gpu/GPU_compute.h new file mode 100644 index 00000000000..a048f72c0a0 --- /dev/null +++ b/source/blender/gpu/GPU_compute.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_sys_types.h" + +#include "GPU_shader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void GPU_compute_dispatch(GPUShader *shader, + uint groups_x_len, + uint groups_y_len, + uint groups_z_len); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h index 76aab3c196b..bdacfe6fc0f 100644 --- a/source/blender/gpu/GPU_index_buffer.h +++ b/source/blender/gpu/GPU_index_buffer.h @@ -49,6 +49,7 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uin /* supports only GPU_PRIM_POINTS, GPU_PRIM_LINES and GPU_PRIM_TRIS. */ void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len); +GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len); void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v); void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *); @@ -70,6 +71,8 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem); GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *); void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, GPUIndexBuf *); +void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding); + /* Create a sub-range of an existing index-buffer. */ GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length); void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, @@ -77,6 +80,15 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, uint start, uint length); +/** + * (Download and) return a pointer containing the data of an index buffer. + * + * Note that the returned pointer is still owned by the driver. To get an + * local copy, use `GPU_indexbuf_unmap` after calling `GPU_indexbuf_read`. + */ +const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem); +uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_data); + void GPU_indexbuf_discard(GPUIndexBuf *elem); bool GPU_indexbuf_is_init(GPUIndexBuf *elem); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 9824c7016dc..3923c920c9e 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -27,6 +27,7 @@ extern "C" { #endif +struct GPUIndexBuf; struct GPUVertBuf; /** Opaque type hiding #blender::gpu::Shader */ @@ -45,6 +46,10 @@ GPUShader *GPU_shader_create(const char *vertcode, const char *libcode, const char *defines, const char *shname); +GPUShader *GPU_shader_create_compute(const char *computecode, + const char *libcode, + const char *defines, + const char *shname); GPUShader *GPU_shader_create_from_python(const char *vertcode, const char *fragcode, const char *geomcode, @@ -53,6 +58,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, const char *geomcode, + const char *computecode, const char *libcode, const char *defines, const eGPUShaderTFBType tf_type, @@ -126,6 +132,7 @@ int GPU_shader_get_uniform(GPUShader *shader, const char *name); int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin); int GPU_shader_get_builtin_block(GPUShader *shader, int builtin); int GPU_shader_get_uniform_block(GPUShader *shader, const char *name); +int GPU_shader_get_ssbo(GPUShader *shader, const char *name); int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name); int GPU_shader_get_texture_binding(GPUShader *shader, const char *name); diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 0687f271670..a338728804c 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -39,6 +39,7 @@ typedef enum eGPUBarrier { GPU_BARRIER_NONE = 0, GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0), GPU_BARRIER_TEXTURE_FETCH = (1 << 1), + GPU_BARRIER_SHADER_STORAGE = (1 << 2), } eGPUBarrier; ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_TEXTURE_FETCH) diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index aae58de533b..2c54016daa7 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -59,6 +59,7 @@ typedef enum { GPU_USAGE_STREAM, GPU_USAGE_STATIC, /* do not keep data in memory */ GPU_USAGE_DYNAMIC, + GPU_USAGE_DEVICE_ONLY, /* Do not do host->device data transfers. */ } GPUUsageType; /** Opaque type hiding blender::gpu::VertBuf. */ @@ -70,6 +71,14 @@ GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *, GPUUsageTyp #define GPU_vertbuf_create_with_format(format) \ GPU_vertbuf_create_with_format_ex(format, GPU_USAGE_STATIC) +/** + * (Download and) return a pointer containing the data of a vertex buffer. + * + * Note that the returned pointer is still owned by the driver. To get an + * local copy, use `GPU_vertbuf_unmap` after calling `GPU_vertbuf_read`. + */ +const void *GPU_vertbuf_read(GPUVertBuf *verts); +void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data); void GPU_vertbuf_clear(GPUVertBuf *verts); void GPU_vertbuf_discard(GPUVertBuf *); @@ -138,6 +147,7 @@ uint GPU_vertbuf_get_vertex_len(const GPUVertBuf *verts); GPUVertBufStatus GPU_vertbuf_get_status(const GPUVertBuf *verts); void GPU_vertbuf_use(GPUVertBuf *); +void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding); /* XXX do not use. */ void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data); diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 04ec82a9213..73792215569 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -47,6 +47,7 @@ class GPUBackend { static GPUBackend *get(void); virtual void samplers_update(void) = 0; + virtual void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) = 0; virtual Context *context_alloc(void *ghost_window) = 0; diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index d8764502800..bedc9ad3092 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -148,6 +148,16 @@ bool GPU_use_hq_normals_workaround(void) return GCaps.use_hq_normals_workaround; } +bool GPU_compute_shader_support(void) +{ + return GCaps.compute_shader_support; +} + +bool GPU_shader_storage_buffer_objects_support(void) +{ + return GCaps.shader_storage_buffer_objects_support; +} + bool GPU_shader_image_load_store_support(void) { return GCaps.shader_image_load_store_support; diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 7c1d4590ce8..ee7ef1e69e6 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -51,6 +51,8 @@ struct GPUCapabilities { const char *(*extension_get)(int); bool mem_stats_support = false; + bool compute_shader_support = false; + bool shader_storage_buffer_objects_support = false; bool shader_image_load_store_support = false; /* OpenGL related workarounds. */ bool mip_render_workaround = false; diff --git a/source/blender/gpu/intern/gpu_compute.cc b/source/blender/gpu/intern/gpu_compute.cc new file mode 100644 index 00000000000..7a8ae2acf9a --- /dev/null +++ b/source/blender/gpu/intern/gpu_compute.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. + */ + +/** \file + * \ingroup gpu + */ + +#include "GPU_compute.h" + +#include "gpu_backend.hh" + +#ifdef __cplusplus +extern "C" { +#endif + +void GPU_compute_dispatch(GPUShader *shader, + uint groups_x_len, + uint groups_y_len, + uint groups_z_len) +{ + blender::gpu::GPUBackend &gpu_backend = *blender::gpu::GPUBackend::get(); + GPU_shader_bind(shader); + gpu_backend.compute_dispatch(groups_x_len, groups_y_len, groups_z_len); +} + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc index 65932d2dbf4..20a26c0fe9d 100644 --- a/source/blender/gpu/intern/gpu_index_buffer.cc +++ b/source/blender/gpu/intern/gpu_index_buffer.cc @@ -31,6 +31,8 @@ #include "gpu_index_buffer_private.hh" +#include + #define KEEP_SINGLE_COPY 1 #define RESTART_INDEX 0xFFFFFFFF @@ -66,6 +68,14 @@ void GPU_indexbuf_init(GPUIndexBufBuilder *builder, GPU_indexbuf_init_ex(builder, prim_type, prim_len * (uint)verts_per_prim, vertex_len); } +GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len) +{ + GPUIndexBuf *elem_ = GPU_indexbuf_calloc(); + IndexBuf *elem = unwrap(elem_); + elem->init_build_on_device(index_len); + return elem_; +} + void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v) { #if TRUST_NO_ONE @@ -241,6 +251,15 @@ void IndexBuf::init(uint indices_len, uint32_t *indices) #endif } +void IndexBuf::init_build_on_device(uint index_len) +{ + is_init_ = true; + index_start_ = 0; + index_len_ = index_len; + index_type_ = GPU_INDEX_U32; + data_ = nullptr; +} + void IndexBuf::init_subrange(IndexBuf *elem_src, uint start, uint length) { /* We don't support nested subranges. */ @@ -307,6 +326,14 @@ void IndexBuf::squeeze_indices_short(uint min_idx, uint max_idx) } } +uint32_t *IndexBuf::unmap(const uint32_t *mapped_memory) const +{ + size_t size = size_get(); + uint32_t *result = static_cast(MEM_mallocN(size, __func__)); + memcpy(result, mapped_memory, size); + return result; +} + } // namespace blender::gpu /** \} */ @@ -351,6 +378,16 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, unwrap(elem)->init_subrange(unwrap(elem_src), start, length); } +const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem) +{ + return unwrap(elem)->read(); +} + +uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer) +{ + return unwrap(elem)->unmap(mapped_buffer); +} + void GPU_indexbuf_discard(GPUIndexBuf *elem) { delete unwrap(elem); @@ -366,4 +403,9 @@ int GPU_indexbuf_primitive_len(GPUPrimType prim_type) return indices_per_primitive(prim_type); } +void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding) +{ + unwrap(elem)->bind_as_ssbo(binding); +} + /** \} */ diff --git a/source/blender/gpu/intern/gpu_index_buffer_private.hh b/source/blender/gpu/intern/gpu_index_buffer_private.hh index 2405db8664a..358258604bf 100644 --- a/source/blender/gpu/intern/gpu_index_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh @@ -75,13 +75,14 @@ class IndexBuf { void init(uint indices_len, uint32_t *indices); void init_subrange(IndexBuf *elem_src, uint start, uint length); + void init_build_on_device(uint index_len); uint32_t index_len_get(void) const { return index_len_; } /* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */ - size_t size_get(void) + size_t size_get(void) const { return index_len_ * to_bytesize(index_type_); }; @@ -91,6 +92,11 @@ class IndexBuf { return is_init_; }; + virtual void bind_as_ssbo(uint binding) = 0; + + virtual const uint32_t *read() const = 0; + uint32_t *unmap(const uint32_t *mapped_memory) const; + private: inline void squeeze_indices_short(uint min_idx, uint max_idx); inline uint index_range(uint *r_min, uint *r_max); @@ -105,6 +111,10 @@ static inline IndexBuf *unwrap(GPUIndexBuf *indexbuf) { return reinterpret_cast(indexbuf); } +static inline const IndexBuf *unwrap(const GPUIndexBuf *indexbuf) +{ + return reinterpret_cast(indexbuf); +} static inline int indices_per_primitive(GPUPrimType prim_type) { diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index aea27756708..265dec7c56a 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -290,6 +290,7 @@ static void standard_defines(Vector &sources) GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, const char *geomcode, + const char *computecode, const char *libcode, const char *defines, const eGPUShaderTFBType tf_type, @@ -297,8 +298,10 @@ GPUShader *GPU_shader_create_ex(const char *vertcode, const int tf_count, const char *shname) { - /* At least a vertex shader and a fragment shader are required. */ - BLI_assert((fragcode != nullptr) && (vertcode != nullptr)); + /* At least a vertex shader and a fragment shader are required, or only a compute shader. */ + BLI_assert(((fragcode != nullptr) && (vertcode != nullptr) && (computecode == nullptr)) || + ((fragcode == nullptr) && (vertcode == nullptr) && (geomcode == nullptr) && + (computecode != nullptr))); Shader *shader = GPUBackend::get()->shader_alloc(shname); @@ -349,6 +352,21 @@ GPUShader *GPU_shader_create_ex(const char *vertcode, shader->geometry_shader_from_glsl(sources); } + if (computecode) { + Vector sources; + standard_defines(sources); + sources.append("#define GPU_COMPUTE_SHADER\n"); + if (defines) { + sources.append(defines); + } + if (libcode) { + sources.append(libcode); + } + sources.append(computecode); + + shader->compute_shader_from_glsl(sources); + } + if (tf_names != nullptr && tf_count > 0) { BLI_assert(tf_type != GPU_SHADER_TFB_NONE); shader->transform_feedback_names_set(Span(tf_names, tf_count), tf_type); @@ -380,8 +398,33 @@ GPUShader *GPU_shader_create(const char *vertcode, const char *defines, const char *shname) { - return GPU_shader_create_ex( - vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, nullptr, 0, shname); + return GPU_shader_create_ex(vertcode, + fragcode, + geomcode, + nullptr, + libcode, + defines, + GPU_SHADER_TFB_NONE, + nullptr, + 0, + shname); +} + +GPUShader *GPU_shader_create_compute(const char *computecode, + const char *libcode, + const char *defines, + const char *shname) +{ + return GPU_shader_create_ex(nullptr, + nullptr, + nullptr, + computecode, + libcode, + defines, + GPU_SHADER_TFB_NONE, + nullptr, + 0, + shname); } GPUShader *GPU_shader_create_from_python(const char *vertcode, @@ -402,6 +445,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, GPUShader *sh = GPU_shader_create_ex(vertcode, fragcode, geomcode, + nullptr, libcode, defines, GPU_SHADER_TFB_NONE, @@ -567,6 +611,13 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin); } +int GPU_shader_get_ssbo(GPUShader *shader, const char *name) +{ + ShaderInterface *interface = unwrap(shader)->interface; + const ShaderInput *ssbo = interface->ssbo_get(name); + return ssbo ? ssbo->location : -1; +} + /* DEPRECATED. */ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) { diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc index c584c40eca8..ae94112b17b 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.cc +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -80,6 +80,8 @@ void ShaderInterface::debug_print() Span attrs = Span(inputs_, attr_len_); Span ubos = Span(inputs_ + attr_len_, ubo_len_); Span uniforms = Span(inputs_ + attr_len_ + ubo_len_, uniform_len_); + Span ssbos = Span(inputs_ + attr_len_ + ubo_len_ + uniform_len_, + ssbo_len_); char *name_buf = name_buffer_; const char format[] = " | %.8x : %4d : %s\n"; @@ -117,6 +119,13 @@ void ShaderInterface::debug_print() } } + if (ssbos.size() > 0) { + printf("\n Shader Storage Objects :\n"); + } + for (const ShaderInput &ssbo : ssbos) { + printf(format, ssbo.name_hash, ssbo.binding, name_buf + ssbo.name_offset); + } + printf("\n"); } diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index aec58544111..ebed7b15170 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -60,6 +60,7 @@ class ShaderInterface { uint attr_len_ = 0; uint ubo_len_ = 0; uint uniform_len_ = 0; + uint ssbo_len_ = 0; /** Enabled bind-points that needs to be fed with data. */ uint16_t enabled_attr_mask_ = 0; uint16_t enabled_ubo_mask_ = 0; @@ -99,6 +100,11 @@ class ShaderInterface { return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, binding); } + inline const ShaderInput *ssbo_get(const char *name) const + { + return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, name); + } + inline const char *input_name_get(const ShaderInput *input) const { return name_buffer_ + input->name_offset; diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh index d9327bbc0f4..281f01dbc22 100644 --- a/source/blender/gpu/intern/gpu_shader_private.hh +++ b/source/blender/gpu/intern/gpu_shader_private.hh @@ -49,6 +49,7 @@ class Shader { virtual void vertex_shader_from_glsl(MutableSpan sources) = 0; virtual void geometry_shader_from_glsl(MutableSpan sources) = 0; virtual void fragment_shader_from_glsl(MutableSpan sources) = 0; + virtual void compute_shader_from_glsl(MutableSpan sources) = 0; virtual bool finalize(void) = 0; virtual void transform_feedback_names_set(Span name_list, diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 09b9eba9f95..3ecbb740a0c 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -149,6 +149,16 @@ GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts_) return wrap(unwrap(verts_)->duplicate()); } +const void *GPU_vertbuf_read(GPUVertBuf *verts) +{ + return unwrap(verts)->read(); +} + +void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data) +{ + return unwrap(verts)->unmap(mapped_data); +} + /** Same as discard but does not free. */ void GPU_vertbuf_clear(GPUVertBuf *verts) { @@ -324,6 +334,11 @@ void GPU_vertbuf_use(GPUVertBuf *verts) unwrap(verts)->upload(); } +void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding) +{ + unwrap(verts)->bind_as_ssbo(binding); +} + /* XXX this is just a wrapper for the use of the Hair refine workaround. * To be used with GPU_vertbuf_use(). */ void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data) diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh index 67a09f6f83c..9531c2c1a5f 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh @@ -66,6 +66,7 @@ class VertBuf { void allocate(uint vert_len); void resize(uint vert_len); void upload(void); + virtual void bind_as_ssbo(uint binding) = 0; VertBuf *duplicate(void); @@ -96,6 +97,8 @@ class VertBuf { } virtual void update_sub(uint start, uint len, void *data) = 0; + virtual const void *read() const = 0; + virtual void *unmap(const void *mapped_data) const = 0; protected: virtual void acquire_data(void) = 0; diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 31b6549fc3b..fb03a2c2d2a 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -437,6 +437,8 @@ 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.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object; /* GL specific capabilities. */ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size); glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GLContext::max_cubemap_size); diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 231e5811b45..e9dcdffced0 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -28,6 +28,7 @@ #include "BLI_vector.hh" #include "gl_batch.hh" +#include "gl_compute.hh" #include "gl_context.hh" #include "gl_drawlist.hh" #include "gl_framebuffer.hh" @@ -126,6 +127,12 @@ class GLBackend : public GPUBackend { return shared_orphan_list_; }; + void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override + { + GLContext::get()->state_manager_active_get()->apply_state(); + GLCompute::dispatch(groups_x_len, groups_y_len, groups_z_len); + } + private: static void platform_init(void); static void platform_exit(void); diff --git a/source/blender/gpu/opengl/gl_compute.cc b/source/blender/gpu/opengl/gl_compute.cc new file mode 100644 index 00000000000..fa8317dde4a --- /dev/null +++ b/source/blender/gpu/opengl/gl_compute.cc @@ -0,0 +1,35 @@ +/* + * 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. + */ + +/** \file + * \ingroup gpu + */ + +#include "gl_compute.hh" + +#include "gl_debug.hh" + +#include "glew-mx.h" + +namespace blender::gpu { + +void GLCompute::dispatch(int group_x_len, int group_y_len, int group_z_len) +{ + glDispatchCompute(group_x_len, group_y_len, group_z_len); + debug::check_gl_error("Dispatch Compute"); +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_compute.hh b/source/blender/gpu/opengl/gl_compute.hh new file mode 100644 index 00000000000..2fd918ddd10 --- /dev/null +++ b/source/blender/gpu/opengl/gl_compute.hh @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +namespace blender::gpu { + +class GLCompute { + public: + static void dispatch(int group_x_len, int group_y_len, int group_z_len); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_index_buffer.cc b/source/blender/gpu/opengl/gl_index_buffer.cc index e2c18c5d0b9..e305f765ad9 100644 --- a/source/blender/gpu/opengl/gl_index_buffer.cc +++ b/source/blender/gpu/opengl/gl_index_buffer.cc @@ -40,17 +40,14 @@ void GLIndexBuf::bind() return; } - if (ibo_id_ == 0) { + const bool allocate_on_device = ibo_id_ == 0; + if (allocate_on_device) { glGenBuffers(1, &ibo_id_); - - if (data_ == nullptr) { - debug::raise_gl_error("Trying to use Index Buffer but the buffer contains no data"); - } } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id_); - if (data_ != nullptr) { + if (data_ != nullptr || allocate_on_device) { size_t size = this->size_get(); /* Sends data to GPU. */ glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data_, GL_STATIC_DRAW); @@ -59,4 +56,29 @@ void GLIndexBuf::bind() } } +void GLIndexBuf::bind_as_ssbo(uint binding) +{ + bind(); + BLI_assert(ibo_id_ != 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ibo_id_); +} + +const uint32_t *GLIndexBuf::read() const +{ + BLI_assert(is_active()); + void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); + uint32_t *result = static_cast(data); + return result; +} + +bool GLIndexBuf::is_active() const +{ + if (!ibo_id_) { + return false; + } + int active_ibo_id = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &active_ibo_id); + return ibo_id_ == active_ibo_id; +} + } // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_index_buffer.hh b/source/blender/gpu/opengl/gl_index_buffer.hh index b84934bb77f..0dbdaa6d398 100644 --- a/source/blender/gpu/opengl/gl_index_buffer.hh +++ b/source/blender/gpu/opengl/gl_index_buffer.hh @@ -34,6 +34,7 @@ namespace blender::gpu { class GLIndexBuf : public IndexBuf { friend class GLBatch; friend class GLDrawList; + friend class GLShader; /* For compute shaders. */ private: GLuint ibo_id_ = 0; @@ -42,6 +43,9 @@ class GLIndexBuf : public IndexBuf { ~GLIndexBuf(); void bind(void); + void bind_as_ssbo(uint binding) override; + + const uint32_t *read() const override; void *offset_ptr(uint additional_vertex_offset) const { @@ -57,6 +61,9 @@ class GLIndexBuf : public IndexBuf { return (index_type_ == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu; } + private: + bool is_active() const; + MEM_CXX_CLASS_ALLOC_FUNCS("GLIndexBuf") }; diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index dd08a67517e..e77347d99eb 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -26,6 +26,7 @@ #include "BLI_string.h" #include "BLI_vector.hh" +#include "GPU_capabilities.h" #include "GPU_platform.h" #include "gl_backend.hh" @@ -63,6 +64,7 @@ GLShader::~GLShader() glDeleteShader(vert_shader_); glDeleteShader(geom_shader_); glDeleteShader(frag_shader_); + glDeleteShader(compute_shader_); glDeleteProgram(shader_program_); } @@ -72,7 +74,7 @@ GLShader::~GLShader() /** \name Shader stage creation * \{ */ -char *GLShader::glsl_patch_get() +static char *glsl_patch_default_get() { /** Used for shader patching. Init once. */ static char patch[512] = "\0"; @@ -111,6 +113,30 @@ char *GLShader::glsl_patch_get() return patch; } +static char *glsl_patch_compute_get() +{ + /** Used for shader patching. Init once. */ + static char patch[512] = "\0"; + if (patch[0] != '\0') { + return patch; + } + + size_t slen = 0; + /* Version need to go first. */ + STR_CONCAT(patch, slen, "#version 430\n"); + STR_CONCAT(patch, slen, "#extension GL_ARB_compute_shader :enable\n"); + BLI_assert(slen < sizeof(patch)); + return patch; +} + +char *GLShader::glsl_patch_get(GLenum gl_stage) +{ + if (gl_stage == GL_COMPUTE_SHADER) { + return glsl_patch_compute_get(); + } + return glsl_patch_default_get(); +} + /* Create, compile and attach the shader stage to the shader program. */ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan sources) { @@ -121,7 +147,7 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan } /* Patch the shader code using the first source slot. */ - sources[0] = glsl_patch_get(); + sources[0] = glsl_patch_get(gl_stage); glShaderSource(shader, sources.size(), sources.data(), nullptr); glCompileShader(shader); @@ -142,6 +168,9 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan case GL_FRAGMENT_SHADER: this->print_log(sources, log, "FragShader", !status); break; + case GL_COMPUTE_SHADER: + this->print_log(sources, log, "ComputeShader", !status); + break; } } } @@ -172,6 +201,11 @@ void GLShader::fragment_shader_from_glsl(MutableSpan sources) frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources); } +void GLShader::compute_shader_from_glsl(MutableSpan sources) +{ + compute_shader_ = this->create_shader_stage(GL_COMPUTE_SHADER, sources); +} + bool GLShader::finalize() { if (compilation_failed_) { diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh index 152eb2f068a..48aaaf2283d 100644 --- a/source/blender/gpu/opengl/gl_shader.hh +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -43,6 +43,7 @@ class GLShader : public Shader { GLuint vert_shader_ = 0; GLuint geom_shader_ = 0; GLuint frag_shader_ = 0; + GLuint compute_shader_ = 0; /** True if any shader failed to compile. */ bool compilation_failed_ = false; @@ -56,6 +57,7 @@ class GLShader : public Shader { void vertex_shader_from_glsl(MutableSpan sources) override; void geometry_shader_from_glsl(MutableSpan sources) override; void fragment_shader_from_glsl(MutableSpan sources) override; + void compute_shader_from_glsl(MutableSpan sources) override; bool finalize(void) override; void transform_feedback_names_set(Span name_list, @@ -75,7 +77,7 @@ class GLShader : public Shader { int program_handle_get(void) const override; private: - char *glsl_patch_get(void); + char *glsl_patch_get(GLenum gl_stage); GLuint create_shader_stage(GLenum gl_stage, MutableSpan sources); diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index 5870c645bf4..9cf072b2e8a 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -29,6 +29,8 @@ #include "gl_shader_interface.hh" +#include "GPU_capabilities.h" + namespace blender::gpu { /* -------------------------------------------------------------------- */ @@ -125,6 +127,18 @@ static inline int image_binding(int32_t program, return -1; } } + +static inline int ssbo_binding(int32_t program, uint32_t ssbo_index) +{ + GLint binding = -1; + GLenum property = GL_BUFFER_BINDING; + GLint values_written = 0; + glGetProgramResourceiv( + program, GL_SHADER_STORAGE_BLOCK, ssbo_index, 1, &property, 1, &values_written, &binding); + + return binding; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -149,6 +163,13 @@ GLShaderInterface::GLShaderInterface(GLuint program) glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); uniform_len = active_uniform_len; + GLint max_ssbo_name_len = 0, ssbo_len = 0; + if (GPU_shader_storage_buffer_objects_support()) { + glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &ssbo_len); + glGetProgramInterfaceiv( + program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &max_ssbo_name_len); + } + BLI_assert(ubo_len <= 16 && "enabled_ubo_mask_ is uint16_t"); /* Work around driver bug with Intel HD 4600 on Windows 7/8, where @@ -162,6 +183,9 @@ GLShaderInterface::GLShaderInterface(GLuint program) if (uniform_len > 0 && max_uniform_name_len == 0) { max_uniform_name_len = 256; } + if (ssbo_len > 0 && max_ssbo_name_len == 0) { + max_ssbo_name_len = 256; + } /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before * allocating the uniform array. */ @@ -186,11 +210,12 @@ GLShaderInterface::GLShaderInterface(GLuint program) } MEM_freeN(ubo_uni_ids); - int input_tot_len = attr_len + ubo_len + uniform_len; + int input_tot_len = attr_len + ubo_len + uniform_len + ssbo_len; inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__); const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len + - uniform_len * max_uniform_name_len; + uniform_len * max_uniform_name_len + + ssbo_len * max_ssbo_name_len; name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); uint32_t name_buffer_offset = 0; @@ -257,6 +282,22 @@ GLShaderInterface::GLShaderInterface(GLuint program) } } + /* SSBOs */ + for (int i = 0; i < ssbo_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + glGetProgramResourceName( + program, GL_SHADER_STORAGE_BLOCK, i, remaining_buffer, &name_len, name); + + const GLint binding = ssbo_binding(program, i); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_++]; + input->binding = input->location = binding; + + name_buffer_offset += this->set_input_name(input, name, name_len); + } + /* Builtin Uniforms */ for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { GPUUniformBuiltin u = static_cast(u_int); diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh index 651c3c22afa..3b4b40b1d10 100644 --- a/source/blender/gpu/opengl/gl_state.hh +++ b/source/blender/gpu/opengl/gl_state.hh @@ -121,6 +121,9 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits) if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) { barrier |= GL_TEXTURE_FETCH_BARRIER_BIT; } + if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) { + barrier |= GL_SHADER_STORAGE_BARRIER_BIT; + } return barrier; } diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index b65686165d9..e2478a9976c 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -368,7 +368,7 @@ void GLTexture::copy_to(Texture *dst_) void *GLTexture::read(int mip, eGPUDataFormat type) { BLI_assert(!(format_flag_ & GPU_FORMAT_COMPRESSED)); - BLI_assert(mip <= mipmaps_); + BLI_assert(mip <= mipmaps_ || mip == 0); BLI_assert(validate_data_format(format_, type)); /* NOTE: mip_size_get() won't override any dimension that is equal to 0. */ diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.cc b/source/blender/gpu/opengl/gl_vertex_buffer.cc index a56d5269fde..ce16a491528 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.cc +++ b/source/blender/gpu/opengl/gl_vertex_buffer.cc @@ -29,6 +29,10 @@ namespace blender::gpu { void GLVertBuf::acquire_data() { + if (usage_ == GPU_USAGE_DEVICE_ONLY) { + return; + } + /* Discard previous data if any. */ MEM_SAFE_FREE(data); data = (uchar *)MEM_mallocN(sizeof(uchar) * this->size_alloc_get(), __func__); @@ -36,6 +40,10 @@ void GLVertBuf::acquire_data() void GLVertBuf::resize_data() { + if (usage_ == GPU_USAGE_DEVICE_ONLY) { + return; + } + data = (uchar *)MEM_reallocN(data, sizeof(uchar) * this->size_alloc_get()); } @@ -94,8 +102,10 @@ void GLVertBuf::bind() vbo_size_ = this->size_used_get(); /* Orphan the vbo to avoid sync then upload data. */ glBufferData(GL_ARRAY_BUFFER, vbo_size_, nullptr, to_gl(usage_)); - glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data); - + /* Do not transfer data from host to device when buffer is device only. */ + if (usage_ != GPU_USAGE_DEVICE_ONLY) { + glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data); + } memory_usage += vbo_size_; if (usage_ == GPU_USAGE_STATIC) { @@ -106,6 +116,37 @@ void GLVertBuf::bind() } } +void GLVertBuf::bind_as_ssbo(uint binding) +{ + bind(); + BLI_assert(vbo_id_ != 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_); +} + +const void *GLVertBuf::read() const +{ + BLI_assert(is_active()); + void *result = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + return result; +} + +void *GLVertBuf::unmap(const void *mapped_data) const +{ + void *result = MEM_mallocN(vbo_size_, __func__); + memcpy(result, mapped_data, vbo_size_); + return result; +} + +bool GLVertBuf::is_active() const +{ + if (!vbo_id_) { + return false; + } + int active_vbo_id = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &active_vbo_id); + return vbo_id_ == active_vbo_id; +} + void GLVertBuf::update_sub(uint start, uint len, void *data) { glBufferSubData(GL_ARRAY_BUFFER, start, len, data); diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh index e2bf6cd00e8..6c38a2225b3 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.hh +++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh @@ -47,12 +47,19 @@ class GLVertBuf : public VertBuf { void update_sub(uint start, uint len, void *data) override; + const void *read() const override; + void *unmap(const void *mapped_data) const override; + protected: void acquire_data(void) override; void resize_data(void) override; void release_data(void) override; void upload_data(void) override; void duplicate_data(VertBuf *dst) override; + void bind_as_ssbo(uint binding) override; + + private: + bool is_active() const; MEM_CXX_CLASS_ALLOC_FUNCS("GLVertBuf"); }; @@ -65,6 +72,7 @@ static inline GLenum to_gl(GPUUsageType type) case GPU_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; case GPU_USAGE_STATIC: + case GPU_USAGE_DEVICE_ONLY: return GL_STATIC_DRAW; default: BLI_assert(0); diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc new file mode 100644 index 00000000000..e8645b89e41 --- /dev/null +++ b/source/blender/gpu/tests/gpu_shader_test.cc @@ -0,0 +1,301 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "GPU_capabilities.h" +#include "GPU_compute.h" +#include "GPU_index_buffer.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_vertex_buffer.h" +#include "GPU_vertex_format.h" + +#include "MEM_guardedalloc.h" + +#include "gpu_testing.hh" + +#include "GPU_glew.h" + +namespace blender::gpu::tests { + +TEST_F(GPUTest, gpu_shader_compute_2d) +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 512; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1, local_size_y = 1) in; +layout(rgba32f, binding = 0) uniform image2D img_output; + +void main() { + vec4 pixel = vec4(1.0, 0.5, 0.2, 1.0); + imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), pixel); +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_2d"); + EXPECT_NE(shader, nullptr); + + /* Create texture to store result and attach to shader. */ + GPUTexture *texture = GPU_texture_create_2d( + "gpu_shader_compute_2d", SIZE, SIZE, 0, GPU_RGBA32F, nullptr); + EXPECT_NE(texture, nullptr); + + GPU_shader_bind(shader); + GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "img_output")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, SIZE, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + float *data = static_cast(GPU_texture_read(texture, GPU_DATA_FLOAT, 0)); + EXPECT_NE(data, nullptr); + for (int index = 0; index < SIZE * SIZE; index++) { + EXPECT_FLOAT_EQ(data[index * 4 + 0], 1.0f); + EXPECT_FLOAT_EQ(data[index * 4 + 1], 0.5f); + EXPECT_FLOAT_EQ(data[index * 4 + 2], 0.2f); + EXPECT_FLOAT_EQ(data[index * 4 + 3], 1.0f); + } + MEM_freeN(data); + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_texture_unbind(texture); + GPU_texture_free(texture); + GPU_shader_free(shader); +} + +TEST_F(GPUTest, gpu_shader_compute_1d) +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 10; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(rgba32f, binding = 1) uniform image1D outputVboData; + +void main() { + int index = int(gl_GlobalInvocationID.x); + vec4 pos = vec4(gl_GlobalInvocationID.x); + imageStore(outputVboData, index, pos); +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_1d"); + EXPECT_NE(shader, nullptr); + + /* Construct Texture. */ + GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, NULL); + EXPECT_NE(texture, nullptr); + + GPU_shader_bind(shader); + GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "outputVboData")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, 1, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + + /* Create texture to load back result. */ + float *data = static_cast(GPU_texture_read(texture, GPU_DATA_FLOAT, 0)); + EXPECT_NE(data, nullptr); + for (int index = 0; index < SIZE; index++) { + float expected_value = index; + EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value); + } + MEM_freeN(data); + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_texture_unbind(texture); + GPU_texture_free(texture); + GPU_shader_free(shader); +} + +TEST_F(GPUTest, gpu_shader_compute_vbo) +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 128; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(std430, binding = 0) writeonly buffer outputVboData +{ + vec4 out_positions[]; +}; + +void main() { + uint index = gl_GlobalInvocationID.x; + vec4 pos = vec4(gl_GlobalInvocationID.x); + out_positions[index] = pos; +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo"); + EXPECT_NE(shader, nullptr); + GPU_shader_bind(shader); + + /* Construct VBO. */ + static GPUVertFormat format = {0}; + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPUVertBuf *vbo = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_DEVICE_ONLY); + GPU_vertbuf_data_alloc(vbo, SIZE); + GPU_vertbuf_bind_as_ssbo(vbo, GPU_shader_get_ssbo(shader, "outputVboData")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, 1, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + + /* Download the vertex buffer. */ + const float *data = static_cast(GPU_vertbuf_read(vbo)); + ASSERT_NE(data, nullptr); + for (int index = 0; index < SIZE; index++) { + float expected_value = index; + EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value); + } + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_vertbuf_discard(vbo); + GPU_shader_free(shader); +} + +TEST_F(GPUTest, gpu_shader_compute_ibo) +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 128; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(std430, binding = 1) writeonly buffer outputIboData +{ + uint out_indexes[]; +}; + +void main() { + uint store_index = int(gl_GlobalInvocationID.x); + out_indexes[store_index] = store_index; +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo"); + EXPECT_NE(shader, nullptr); + GPU_shader_bind(shader); + + /* Construct IBO. */ + GPUIndexBuf *ibo = GPU_indexbuf_build_on_device(SIZE); + GPU_indexbuf_bind_as_ssbo(ibo, GPU_shader_get_ssbo(shader, "outputIboData")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, 1, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + + /* Download the index buffer. */ + const uint32_t *data = GPU_indexbuf_read(ibo); + ASSERT_NE(data, nullptr); + for (int index = 0; index < SIZE; index++) { + uint32_t expected = index; + EXPECT_EQ(data[index], expected); + } + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_indexbuf_discard(ibo); + GPU_shader_free(shader); +} + +TEST_F(GPUTest, gpu_shader_ssbo_binding) +{ + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer ssboBinding0 +{ + int data0[]; +}; +layout(std430, binding = 1) buffer ssboBinding1 +{ + int data1[]; +}; + +void main() { +} + +)"; + + GPUShader *shader = GPU_shader_create_compute(compute_glsl, nullptr, nullptr, "gpu_shader_ssbo"); + EXPECT_NE(shader, nullptr); + GPU_shader_bind(shader); + + EXPECT_EQ(0, GPU_shader_get_ssbo(shader, "ssboBinding0")); + EXPECT_EQ(1, GPU_shader_get_ssbo(shader, "ssboBinding1")); + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_shader_free(shader); +} + +} // namespace blender::gpu::tests -- cgit v1.2.3 From 8f9599d17e80254928d2d72081a4c7e0dee64038 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 26 May 2021 17:02:32 +0200 Subject: DrawManager: Use Compute Shader to Update Hair. This patch will use compute shaders to create the VBO for hair. The previous implementation uses tranform feedback. Timings master (transform feedback with GPU_USAGE_STATIC between 0.000069s and 0.000362s Timings transform feedback with GPU_USAGE_DEVICE_ONLY. between 0.000057s and 0.000122s Timings compute shader between 0.000032 and 0.000092s Future improvements: * Generate hair Index buffer using compute shaders: currently done single threaded on CPU, easy to add as compute shader. Reviewed By: fclem Differential Revision: https://developer.blender.org/D11057 --- source/blender/draw/CMakeLists.txt | 1 + source/blender/draw/intern/DRW_render.h | 7 ++ source/blender/draw/intern/draw_cache_impl_hair.c | 3 +- source/blender/draw/intern/draw_hair.c | 135 +++++++++++++++------ source/blender/draw/intern/draw_manager.h | 13 ++ source/blender/draw/intern/draw_manager_data.c | 36 ++++++ source/blender/draw/intern/draw_manager_exec.c | 10 ++ .../draw/intern/shaders/common_hair_lib.glsl | 78 +++++++++++- .../intern/shaders/common_hair_refine_comp.glsl | 24 ++++ .../intern/shaders/common_hair_refine_vert.glsl | 45 +------ source/blender/gpu/GPU_capabilities.h | 2 + source/blender/gpu/intern/gpu_capabilities.cc | 10 ++ .../blender/gpu/intern/gpu_capabilities_private.hh | 2 + source/blender/gpu/opengl/gl_backend.cc | 8 ++ 14 files changed, 292 insertions(+), 82 deletions(-) create mode 100644 source/blender/draw/intern/shaders/common_hair_refine_comp.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 045adf4b380..95c0f5d300c 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -321,6 +321,7 @@ data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC) data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC) +data_to_c_simple(intern/shaders/common_hair_refine_comp.glsl SRC) data_to_c_simple(intern/shaders/common_math_lib.glsl SRC) data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC) data_to_c_simple(intern/shaders/common_view_lib.glsl SRC) diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 2545cfa65dc..5071658fd82 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -438,6 +438,10 @@ void DRW_shgroup_call_range( void DRW_shgroup_call_instance_range( DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct); +void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, + int groups_x_len, + int groups_y_len, + int groups_z_len); void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count); void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count); void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count); @@ -575,6 +579,9 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, const char *name, const float (*value)[4], int arraysize); +void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf *vertex_buffer); bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup); diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c index fd28ac00186..6424b21666d 100644 --- a/source/blender/draw/intern/draw_cache_impl_hair.c +++ b/source/blender/draw/intern/draw_cache_impl_hair.c @@ -243,7 +243,8 @@ static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *c GPUVertFormat format = {0}; GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format(&format); + cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format_ex(&format, + GPU_USAGE_DEVICE_ONLY); /* Create a destination buffer for the transform feedback. Sized appropriately */ /* Those are points! not line segments. */ diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index bca227a24e2..258777b34fb 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -36,15 +36,28 @@ #include "BKE_duplilist.h" #include "GPU_batch.h" +#include "GPU_capabilities.h" +#include "GPU_compute.h" #include "GPU_shader.h" +#include "GPU_texture.h" #include "GPU_vertex_buffer.h" #include "draw_hair_private.h" #ifndef __APPLE__ # define USE_TRANSFORM_FEEDBACK +# define USE_COMPUTE_SHADERS #endif +BLI_INLINE bool drw_hair_use_compute_shaders(void) +{ +#ifdef USE_COMPUTE_SHADERS + return GPU_compute_shader_support(); +#else + return false; +#endif +} + typedef enum ParticleRefineShader { PART_REFINE_CATMULL_ROM = 0, PART_REFINE_MAX_SHADER, @@ -71,6 +84,7 @@ static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in t extern char datatoc_common_hair_lib_glsl[]; extern char datatoc_common_hair_refine_vert_glsl[]; +extern char datatoc_common_hair_refine_comp_glsl[]; extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; static GPUShader *hair_refine_shader_get(ParticleRefineShader sh) @@ -79,15 +93,26 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader sh) return g_refine_shaders[sh]; } - char *vert_with_lib = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); +#ifdef USE_COMPUTE_SHADERS + const bool do_compute = drw_hair_use_compute_shaders(); + if (do_compute) { + g_refine_shaders[sh] = GPU_shader_create_compute(datatoc_common_hair_refine_comp_glsl, + datatoc_common_hair_lib_glsl, + "#define HAIR_PHASE_SUBDIV\n", + __func__); + return g_refine_shaders[sh]; + } +#endif #ifdef USE_TRANSFORM_FEEDBACK + char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, + datatoc_common_hair_refine_vert_glsl); const char *var_names[1] = {"finalColor"}; g_refine_shaders[sh] = DRW_shader_create_with_transform_feedback( - vert_with_lib, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); + shader_src, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); + #else - g_refine_shaders[sh] = DRW_shader_create(vert_with_lib, + g_refine_shaders[sh] = DRW_shader_create(shader_src, NULL, datatoc_gpu_shader_3D_smooth_color_frag_glsl, "#define blender_srgb_to_framebuffer_space(a) a\n" @@ -95,14 +120,14 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader sh) "#define TF_WORKAROUND\n"); #endif - MEM_freeN(vert_with_lib); + MEM_freeN(shader_src); return g_refine_shaders[sh]; } void DRW_hair_init(void) { -#ifdef USE_TRANSFORM_FEEDBACK +#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) g_tf_pass = DRW_pass_create("Update Hair Pass", 0); #else g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR); @@ -125,6 +150,67 @@ void DRW_hair_init(void) } } +static void drw_hair_particle_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, + ParticleHairCache *cache, + const int subdiv) +{ + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex); + DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex); + DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); + DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); +} + +static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, const int subdiv) +{ + const int strands_len = cache->strands_len; + const int final_points_len = cache->final[subdiv].strands_res * strands_len; + if (final_points_len > 0) { + GPUShader *shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); + DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); + drw_hair_particle_cache_shgrp_attach_resources(shgrp, cache, subdiv); + DRW_shgroup_vertex_buffer(shgrp, "hairPointOutputBuffer", cache->final[subdiv].proc_buf); + + const int max_strands_per_call = GPU_max_work_group_count(0); + int strands_start = 0; + while (strands_start < strands_len) { + int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); + DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); + DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); + DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); + strands_start += batch_strands_len; + } + } +} + +static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache *cache, + const int subdiv) +{ + const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; + if (final_points_len > 0) { + GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); + +#ifdef USE_TRANSFORM_FEEDBACK + DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( + tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); +#else + DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); + + ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); + pr_call->next = g_tf_calls; + pr_call->vbo = cache->final[subdiv].proc_buf; + pr_call->shgrp = tf_shgrp; + pr_call->vert_len = final_points_len; + g_tf_calls = pr_call; + DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); + DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); + DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); +#endif + + drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); + DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); + } +} + static ParticleHairCache *drw_hair_particle_cache_get( Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res) { @@ -140,32 +226,11 @@ static ParticleHairCache *drw_hair_particle_cache_get( } if (update) { - int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; - if (final_points_len > 0) { - GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); - -#ifdef USE_TRANSFORM_FEEDBACK - DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( - tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); -#else - DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - - ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); - pr_call->next = g_tf_calls; - pr_call->vbo = cache->final[subdiv].proc_buf; - pr_call->shgrp = tf_shgrp; - pr_call->vert_len = final_points_len; - g_tf_calls = pr_call; - DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); - DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); - DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); -#endif - - DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", cache->point_tex); - DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", cache->strand_tex); - DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); - DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); - DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); + if (drw_hair_use_compute_shaders()) { + drw_hair_particle_cache_update_compute(cache, subdiv); + } + else { + drw_hair_particle_cache_update_transform_feedback(cache, subdiv); } } return cache; @@ -367,9 +432,11 @@ void DRW_hair_update(void) MEM_freeN(data); GPU_framebuffer_free(fb); #else - /* TODO(fclem): replace by compute shader. */ - /* Just render using transform feedback. */ + /* Just render the pass when using compute shaders or transform feedback. */ DRW_draw_pass(g_tf_pass); + if (drw_hair_use_compute_shaders()) { + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + } #endif } diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 84bc0327aa2..d4e22c83798 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -187,6 +187,10 @@ typedef enum { DRW_CMD_DRAW_INSTANCE = 2, DRW_CMD_DRAW_INSTANCE_RANGE = 3, DRW_CMD_DRAW_PROCEDURAL = 4, + + /* Compute Commands. */ + DRW_CMD_COMPUTE = 8, + /* Other Commands */ DRW_CMD_CLEAR = 12, DRW_CMD_DRWSTATE = 13, @@ -224,6 +228,12 @@ typedef struct DRWCommandDrawInstanceRange { uint inst_count; } DRWCommandDrawInstanceRange; +typedef struct DRWCommandCompute { + int groups_x_len; + int groups_y_len; + int groups_z_len; +} DRWCommandCompute; + typedef struct DRWCommandDrawProcedural { GPUBatch *batch; DRWResourceHandle handle; @@ -260,6 +270,7 @@ typedef union DRWCommand { DRWCommandDrawInstance instance; DRWCommandDrawInstanceRange instance_range; DRWCommandDrawProcedural procedural; + DRWCommandCompute compute; DRWCommandSetMutableState state; DRWCommandSetStencil stencil; DRWCommandSetSelectID select_id; @@ -274,6 +285,7 @@ struct DRWCallBuffer { }; /** Used by #DRWUniform.type */ +/* TODO(jbakker): rename to DRW_RESOURCE/DRWResourceType. */ typedef enum { DRW_UNIFORM_INT = 0, DRW_UNIFORM_INT_COPY, @@ -286,6 +298,7 @@ typedef enum { DRW_UNIFORM_BLOCK, DRW_UNIFORM_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, + DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, /** Per drawcall uniforms/UBO */ DRW_UNIFORM_BLOCK_OBMATS, DRW_UNIFORM_BLOCK_OBINFOS, diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 6bdc5305fed..3b852e7f8c8 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -47,6 +47,7 @@ #endif #include "GPU_buffers.h" +#include "GPU_capabilities.h" #include "GPU_material.h" #include "GPU_uniform_buffer.h" @@ -446,6 +447,19 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, } } +void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf *vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + BLI_assert(false && "Unable to locate binding of shader storage buffer objects."); + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -700,6 +714,17 @@ static void drw_command_draw_intance_range( cmd->inst_count = count; } +static void drw_command_compute(DRWShadingGroup *shgroup, + int groups_x_len, + int groups_y_len, + int groups_z_len) +{ + DRWCommandCompute *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE); + cmd->groups_x_len = groups_x_len; + cmd->groups_y_len = groups_y_len; + cmd->groups_z_len = groups_z_len; +} + static void drw_command_draw_procedural(DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, @@ -815,6 +840,17 @@ void DRW_shgroup_call_instance_range( drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); } +void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, + int groups_x_len, + int groups_y_len, + int groups_z_len) +{ + BLI_assert(groups_x_len > 0 && groups_y_len > 0 && groups_z_len > 0); + BLI_assert(GPU_compute_shader_support()); + + drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len); +} + static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup, GPUBatch *geom, Object *ob, diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 4c8fcb0e016..f29caebeb84 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -29,6 +29,7 @@ #include "BKE_global.h" +#include "GPU_compute.h" #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_state.h" @@ -672,6 +673,9 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, ((GPUVertBuf *)uni->pvalue)); break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE: + GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location); + break; /* Legacy/Fallback support. */ case DRW_UNIFORM_BASE_INSTANCE: state->baseinst_loc = uni->location; @@ -1050,6 +1054,12 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) cmd->instance_range.inst_count, false); break; + case DRW_CMD_COMPUTE: + GPU_compute_dispatch(shgroup->shader, + cmd->compute.groups_x_len, + cmd->compute.groups_y_len, + cmd->compute.groups_z_len); + break; } } diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index 8684d82f228..02c335ddae2 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -28,6 +28,9 @@ uniform bool hairCloseTip = true; uniform vec4 hairDupliMatrix[4]; +/* Strand batch offset when used in compute shaders. */ +uniform int hairStrandOffset = 0; + /* -- Per control points -- */ uniform samplerBuffer hairPointBuffer; /* RGBA32F */ #define point_position xyz @@ -43,13 +46,37 @@ uniform usamplerBuffer hairStrandSegBuffer; /* R16UI */ /* -- Subdivision stage -- */ /** - * We use a transform feedback to preprocess the strands and add more subdivision to it. - * For the moment these are simple smooth interpolation but one could hope to see the full + * We use a transform feedback or compute shader to preprocess the strands and add more subdivision + * to it. For the moment these are simple smooth interpolation but one could hope to see the full * children particle modifiers being evaluated at this stage. * * If no more subdivision is needed, we can skip this step. */ +#ifdef GPU_VERTEX_SHADER +float hair_get_local_time() +{ + return float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1); +} + +int hair_get_id() +{ + return gl_VertexID / hairStrandsRes; +} +#endif + +#ifdef GPU_COMPUTE_SHADER +float hair_get_local_time() +{ + return float(gl_GlobalInvocationID.y) / float(hairStrandsRes - 1); +} + +int hair_get_id() +{ + return int(gl_GlobalInvocationID.x) + hairStrandOffset; +} +#endif + #ifdef HAIR_PHASE_SUBDIV int hair_get_base_id(float local_time, int strand_segments, out float interp_time) { @@ -64,9 +91,9 @@ int hair_get_base_id(float local_time, int strand_segments, out float interp_tim void hair_get_interp_attrs( out vec4 data0, out vec4 data1, out vec4 data2, out vec4 data3, out float interp_time) { - float local_time = float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1); + float local_time = hair_get_local_time(); - int hair_id = gl_VertexID / hairStrandsRes; + int hair_id = hair_get_id(); int strand_offset = int(texelFetch(hairStrandBuffer, hair_id).x); int strand_segments = int(texelFetch(hairStrandSegBuffer, hair_id).x); @@ -96,6 +123,7 @@ void hair_get_interp_attrs( */ #if !defined(HAIR_PHASE_SUBDIV) && defined(GPU_VERTEX_SHADER) + int hair_get_strand_id(void) { return gl_VertexID / (hairStrandsRes * hairThicknessRes); @@ -227,3 +255,45 @@ vec2 hair_resolve_barycentric(vec2 vert_barycentric) return vec2(1.0 - vert_barycentric.x, 0.0); } } + +/* Hair interpolation functions. */ +vec4 hair_get_weights_cardinal(float t) +{ + float t2 = t * t; + float t3 = t2 * t; +#if defined(CARDINAL) + float fc = 0.71; +#else /* defined(CATMULL_ROM) */ + float fc = 0.5; +#endif + + vec4 weights; + /* GLSL Optimized version of key_curve_position_weights() */ + float fct = t * fc; + float fct2 = t2 * fc; + float fct3 = t3 * fc; + weights.x = (fct2 * 2.0 - fct3) - fct; + weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0; + weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct; + weights.w = fct3 - fct2; + return weights; +} + +/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */ +vec4 hair_get_weights_bspline(float t) +{ + float t2 = t * t; + float t3 = t2 * t; + + vec4 weights; + /* GLSL Optimized version of key_curve_position_weights() */ + weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666); + weights.y = (0.5 * t3 - t2 + 0.66666666); + weights.w = (0.16666666 * t3); + return weights; +} + +vec4 hair_interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w) +{ + return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w; +} diff --git a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl new file mode 100644 index 00000000000..4dcde4b0245 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl @@ -0,0 +1,24 @@ + +/* + * To be compiled with common_hair_lib.glsl. + */ + +layout(local_size_x = 1, local_size_y = 1) in; +layout(std430, binding = 0) writeonly buffer hairPointOutputBuffer +{ + vec4 posTime[]; +} +out_vertbuf; + +void main(void) +{ + float interp_time; + vec4 data0, data1, data2, data3; + hair_get_interp_attrs(data0, data1, data2, data3, interp_time); + + vec4 weights = hair_get_weights_cardinal(interp_time); + vec4 result = hair_interp_data(data0, data1, data2, data3, weights); + + uint index = uint(hair_get_id() * hairStrandsRes) + gl_GlobalInvocationID.y; + out_vertbuf.posTime[index] = result; +} diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl index 3f5e3f8226f..371d43827b9 100644 --- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl +++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl @@ -3,47 +3,6 @@ out vec4 finalColor; -vec4 get_weights_cardinal(float t) -{ - float t2 = t * t; - float t3 = t2 * t; -#if defined(CARDINAL) - float fc = 0.71; -#else /* defined(CATMULL_ROM) */ - float fc = 0.5; -#endif - - vec4 weights; - /* GLSL Optimized version of key_curve_position_weights() */ - float fct = t * fc; - float fct2 = t2 * fc; - float fct3 = t3 * fc; - weights.x = (fct2 * 2.0 - fct3) - fct; - weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0; - weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct; - weights.w = fct3 - fct2; - return weights; -} - -/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */ -vec4 get_weights_bspline(float t) -{ - float t2 = t * t; - float t3 = t2 * t; - - vec4 weights; - /* GLSL Optimized version of key_curve_position_weights() */ - weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666); - weights.y = (0.5 * t3 - t2 + 0.66666666); - weights.w = (0.16666666 * t3); - return weights; -} - -vec4 interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w) -{ - return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w; -} - #ifdef TF_WORKAROUND uniform int targetWidth; uniform int targetHeight; @@ -56,8 +15,8 @@ void main(void) vec4 data0, data1, data2, data3; hair_get_interp_attrs(data0, data1, data2, data3, interp_time); - vec4 weights = get_weights_cardinal(interp_time); - finalColor = interp_data(data0, data1, data2, data3, weights); + vec4 weights = hair_get_weights_cardinal(interp_time); + finalColor = hair_interp_data(data0, data1, data2, data3, weights); #ifdef TF_WORKAROUND int id = gl_VertexID - idOffset; diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index 45c656b49be..0c054d4f264 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -37,6 +37,8 @@ int GPU_max_textures(void); int GPU_max_textures_vert(void); int GPU_max_textures_geom(void); int GPU_max_textures_frag(void); +int GPU_max_work_group_count(int index); +int GPU_max_work_group_size(int index); int GPU_max_uniforms_vert(void); int GPU_max_uniforms_frag(void); int GPU_max_batch_indices(void); diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index bedc9ad3092..c6e9dc210cb 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -82,6 +82,16 @@ int GPU_max_textures(void) return GCaps.max_textures; } +int GPU_max_work_group_count(int index) +{ + return GCaps.max_work_group_count[index]; +} + +int GPU_max_work_group_size(int index) +{ + return GCaps.max_work_group_size[index]; +} + int GPU_max_uniforms_vert(void) { return GCaps.max_uniforms_vert; diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index ee7ef1e69e6..95cf7fd335d 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -41,6 +41,8 @@ struct GPUCapabilities { int max_textures_vert = 0; int max_textures_geom = 0; int max_textures_frag = 0; + int max_work_group_count[3] = {0, 0, 0}; + int max_work_group_size[3] = {0, 0, 0}; int max_uniforms_vert = 0; int max_uniforms_frag = 0; int max_batch_indices = 0; diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index fb03a2c2d2a..d85f9f7684d 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -438,6 +438,14 @@ 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; + 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]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &GCaps.max_work_group_count[2]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &GCaps.max_work_group_size[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &GCaps.max_work_group_size[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]); + } GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object; /* GL specific capabilities. */ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size); -- cgit v1.2.3 From ee849ca0f8f60c142dce7ecea1be74d382247c12 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 26 May 2021 11:45:27 +0200 Subject: ID management: Do not assume that `NO_MAIN` means `NO_USER_REFCOUNT` While this is still very fuzzy in current code, this old behavior makes it close to impossible to efficiently use out-of-main temp data, as it implies that we'd need to update refcounts everytime we add something back into BMain (an 'un-refcount' ID usages when removing from BMain). Now that we have two separate flags/tags for those two different things, let's not merge them anymore. Note that this is somewhat on-going process, still needs more checks and cleanup. Related to T88555. --- source/blender/blenkernel/intern/lib_id.c | 8 -------- source/blender/blenkernel/intern/lib_query.c | 7 ++++--- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index c2e5006cbc1..8ad524dea21 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1221,14 +1221,6 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || bmain != NULL); BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_NO_ALLOCATE) == 0); BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_LOCAL) == 0); - if (!is_private_id_data) { - /* When we are handling private ID data, we might still want to manage usercounts, even - * though that ID data-block is actually outside of Main... */ - BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || - (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0); - } - /* Never implicitly copy shapekeys when generating temp data outside of Main database. */ - BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || (flag & LIB_ID_COPY_SHAPEKEY) == 0); /* 'Private ID' data handling. */ if ((bmain != NULL) && is_private_id_data) { diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 3281783d81a..b748061ef8a 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -244,9 +244,10 @@ static void library_foreach_ID_link(Main *bmain, * (the node tree), but re-use those generated for the 'owner' ID (the material). */ if (inherit_data == NULL) { data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0; - /* When an ID is not in Main database, it should never refcount IDs it is using. - * Exceptions: NodeTrees (yeah!) directly used by Materials. */ - data.cb_flag_clear = (id->tag & LIB_TAG_NO_MAIN) ? IDWALK_CB_USER | IDWALK_CB_USER_ONE : 0; + /* When an ID is defined as not refcounting its ID usages, it should never do it. */ + data.cb_flag_clear = (id->tag & LIB_TAG_NO_USER_REFCOUNT) ? + IDWALK_CB_USER | IDWALK_CB_USER_ONE : + 0; } else { data.cb_flag = inherit_data->cb_flag; -- cgit v1.2.3 From 6cbe5dd1c3d9cba182c4a8785e921d61f9e8c207 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 26 May 2021 11:52:24 +0200 Subject: ID management: remapping: add flag to enforce refcounting handling. While indeally we should only skip refcounting when relevant tag is set, doing this in remapping code is too risky for now. Related to previous commit and T88555. --- source/blender/blenkernel/BKE_lib_remap.h | 4 ++++ source/blender/blenkernel/intern/lib_remap.c | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index 705d2b030e5..e806dedc14c 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -85,6 +85,10 @@ enum { * freed ones). */ ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS = 1 << 7, + /** Force handling user count even for IDs that are outside of Main (used in some cases when + * dealing with IDs temporarily out of Main, but which will be put in it ultimately). + */ + ID_REMAP_FORCE_USER_REFCOUNT = 1 << 8, }; /* Note: Requiring new_id to be non-null, this *may* not be the case ultimately, diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index b32b97dc250..2641208897e 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -137,6 +137,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0; const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0; + const bool force_user_refcount = (id_remap_data->flag & ID_REMAP_FORCE_USER_REFCOUNT) != 0; #ifdef DEBUG_PRINT printf( @@ -203,16 +204,16 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) } } if (cb_flag & IDWALK_CB_USER) { - /* NOTE: We don't user-count IDs which are not in the main database. + /* NOTE: by default we don't user-count IDs which are not in the main database. * This is because in certain conditions we can have data-blocks in * the main which are referencing data-blocks outside of it. * For example, BKE_mesh_new_from_object() called on an evaluated * object will cause such situation. */ - if ((old_id->tag & LIB_TAG_NO_MAIN) == 0) { + if (force_user_refcount || (old_id->tag & LIB_TAG_NO_MAIN) == 0) { id_us_min(old_id); } - if (new_id != NULL && (new_id->tag & LIB_TAG_NO_MAIN) == 0) { + if (new_id != NULL && (force_user_refcount || (new_id->tag & LIB_TAG_NO_MAIN) == 0)) { /* We do not want to handle LIB_TAG_INDIRECT/LIB_TAG_EXTERN here. */ new_id->us++; } -- cgit v1.2.3 From 653d39cec3fe2127e575edfa0b69dc75385d6c26 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 26 May 2021 12:43:34 +0200 Subject: LibOverride: Do not try to generate override data of linked data. This is obviously not saved, and should never be editable, so was only a waste of time. --- source/blender/blenkernel/intern/lib_override.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 8341c5b6e78..3bc2725d10f 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1961,7 +1961,7 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH); FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && + if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) && (force_auto || (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) { /* Usual issue with pose, it's quiet rare but sometimes they may not be up to date when this * function is called. */ -- cgit v1.2.3 From daf39af70afd88e17757450a6b93c953c30f51a1 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 26 May 2021 15:43:12 +0200 Subject: IDManagement: Shapekey: add a `owner_get` callback. Even though shepkeys are not strictly speaking an embedded data, they share quiet a few points with those, and from liboverride perspective they are embedded, so... --- source/blender/blenkernel/intern/key.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index f2893e162cb..073276b7011 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -105,6 +105,11 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_ID(data, key->from, IDWALK_CB_LOOPBACK); } +static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id) +{ + return ((Key *)id)->from; +} + static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Key *key = (Key *)id; @@ -216,7 +221,9 @@ IDTypeInfo IDType_ID_KE = { .make_local = NULL, .foreach_id = shapekey_foreach_id, .foreach_cache = NULL, - .owner_get = NULL, /* Could have one actually? */ + /* A bit weird, due to shapekeys not being strictly speaking embedded data... But they also + * share a lot with those (non linkable, only ever used by one owner ID, etc.). */ + .owner_get = shapekey_owner_get, .blend_write = shapekey_blend_write, .blend_read_data = shapekey_blend_read_data, -- cgit v1.2.3 From 51ca9833d9da95c4342cc0b87a8757639499d1e4 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 26 May 2021 15:44:51 +0200 Subject: LibOverride: add helper to retrieve override data from an ID. Embedded IDs do not own their own override data, but rather use the one from their owner. --- source/blender/blenkernel/intern/lib_override.c | 26 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 3bc2725d10f..1d7c4c3ef6b 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -80,6 +80,19 @@ static void lib_override_library_property_clear(IDOverrideLibraryProperty *op); static void lib_override_library_property_operation_clear( IDOverrideLibraryPropertyOperation *opop); +/** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */ +BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id) +{ + if (id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) { + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (id_type->owner_get != NULL) { + return id_type->owner_get(bmain, id)->override_library; + } + BLI_assert(!"IDTypeInfo of liboverride-embedded ID with no owner getter"); + } + return id->override_library; +} + /** Initialize empty overriding of \a reference_id by \a local_id. */ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id) { @@ -592,19 +605,14 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data /* Do not tag 'virtual' overrides (shape keys here, as we already rejected embedded case * above). */ if (ID_IS_OVERRIDE_LIBRARY_REAL(to_id)) { - Library *reference_lib = NULL; - if (GS(id_owner->name) == ID_KE) { - reference_lib = ((Key *)id_owner)->from->override_library->reference->lib; - } - else { - reference_lib = id_owner->override_library->reference->lib; - } - if (to_id->override_library->reference->lib != reference_lib) { + Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib; + ID *to_id_reference = lib_override_get(bmain, to_id)->reference; + if (to_id_reference->lib != reference_lib) { /* We do not override data-blocks from other libraries, nor do we process them. */ continue; } - if (to_id->override_library->reference->tag & LIB_TAG_MISSING) { + if (to_id_reference->tag & LIB_TAG_MISSING) { to_id->tag |= missing_tag; } else { -- cgit v1.2.3 From 72d660443bdb8ed0b8ab34fdfe068d4e065d7c58 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 26 May 2021 16:53:59 +0200 Subject: LibOverride: add recursive resync. Recursive resync means also resyncing overrides that are linked from other library files into current working file. Note that this allows to get 'working' files even when their dependencies are out of sync. However, since linked data is never written/saved, this has to be re-done every time the working file is loaded, until said dependencies are updated properly. NOTE: This is still missing the 'report' side of things, which is part of a larger task to enhance reports regarding both linking, and liboverrides (see T88393). ---------- Technical notes: Implementing this proved to be slightly more challenging than expected, mainly because one of the key aspects of the feature was never done in Blender before: manipulating, re-creating linked data. This ended up moving the whole resync code to use temp IDs out of bmain, which is better in the long run anyway (and more aligned with what we generally want to do when manipulating temp ID data). It should also give a marginal improvement in performances for regular resync. This commit also had to carefully 'sort' libraries by level of indirect usage, as we want to resync first the libraries that are the least directly used, i.e. libraries that are most used by other libraries. --- source/blender/blenkernel/BKE_lib_override.h | 5 +- source/blender/blenkernel/intern/lib_override.c | 432 +++++++++++++++++------- source/blender/makesdna/DNA_ID.h | 2 +- 3 files changed, 310 insertions(+), 129 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index f1ed5a453ba..0275c2c235c 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -47,6 +47,7 @@ struct ID; struct IDOverrideLibrary; struct IDOverrideLibraryProperty; struct IDOverrideLibraryPropertyOperation; +struct Library; struct Main; struct Object; struct PointerRNA; @@ -68,7 +69,9 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id); struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, struct ID *reference_id, const bool do_tagged_remap); -bool BKE_lib_override_library_create_from_tag(struct Main *bmain); +bool BKE_lib_override_library_create_from_tag(struct Main *bmain, + const struct Library *reference_library, + const bool do_no_main); bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 1d7c4c3ef6b..36730a7baf7 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -48,6 +48,7 @@ #include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_node.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -207,12 +208,17 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo *override = NULL; } -static ID *lib_override_library_create_from(Main *bmain, ID *reference_id) +static ID *lib_override_library_create_from(Main *bmain, + ID *reference_id, + const int lib_id_copy_flags) { /* Note: We do not want to copy possible override data from reference here (whether it is an * override template, or already an override of some other ref data). */ - ID *local_id = BKE_id_copy_ex( - bmain, reference_id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE); + ID *local_id = BKE_id_copy_ex(bmain, + reference_id, + NULL, + LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE | + lib_id_copy_flags); if (local_id == NULL) { return NULL; @@ -270,7 +276,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, BLI_assert(reference_id != NULL); BLI_assert(reference_id->lib != NULL); - ID *local_id = lib_override_library_create_from(bmain, reference_id); + ID *local_id = lib_override_library_create_from(bmain, reference_id, 0); if (do_tagged_remap) { Key *reference_key, *local_key = NULL; @@ -315,9 +321,17 @@ 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 + * (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. + * * \return \a true on success, \a false otherwise. */ -bool BKE_lib_override_library_create_from_tag(Main *bmain) +bool BKE_lib_override_library_create_from_tag(Main *bmain, + const Library *reference_library, + const bool do_no_main) { ID *reference_id; bool success = true; @@ -327,7 +341,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) /* Get all IDs we want to override. */ FOREACH_MAIN_ID_BEGIN (bmain, reference_id) { - if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL && + if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib == reference_library && BKE_idtype_idcode_is_linkable(GS(reference_id->name))) { todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__); todo_id_iter->data = reference_id; @@ -339,10 +353,16 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) /* Override the IDs. */ for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { reference_id = todo_id_iter->data; + + /* If `newid` is already set, assume it has been handled by calling code. + * Only current use case: re-using proxy ID when converting to liboverride. */ if (reference_id->newid == NULL) { - /* If `newid` is already set, assume it has been handled by calling code. - * Only current use case: re-using proxy ID when converting to liboverride. */ - if ((reference_id->newid = lib_override_library_create_from(bmain, reference_id)) == NULL) { + /* Note: `no main` case is used during resync procedure, to support recursive resync. This + * requires extra care further odwn the resync process, see + * `BKE_lib_override_library_resync`. */ + reference_id->newid = lib_override_library_create_from( + bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0); + if (reference_id->newid == NULL) { success = false; break; } @@ -382,23 +402,42 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) /* Still checking the whole Main, that way we can tag other local IDs as needing to be * remapped to use newly created overriding IDs, if needed. */ - FOREACH_MAIN_ID_BEGIN (bmain, other_id) { - if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) { - /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap - * local IDs usages anyway. */ + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + /* In case we created new overrides as 'no main', they are not accessible directly in this + * loop, but we can get to them through their reference's `newid` pointer. */ + if (do_no_main && id->lib == reference_id->lib && id->newid != NULL) { + other_id = id->newid; + /* Otherwise we cannot properly dinstinguish between IDs that are actually from the + * linked library (and should not be remapped), and IDs that are overrides re-generated + * from the reference from the linked library, and must therefore be remapped. + * + * This is reset afterwards at the end of this loop. */ + other_id->lib = NULL; + } + else { + other_id = id; + } + + /* If other ID is a linked one, but not from the same library as our reference, then we + * consider we should also remap it, as part of recursive resync. */ + if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib) { BKE_libblock_relink_ex(bmain, other_id, reference_id, local_id, - ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); + ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT); if (reference_key != NULL) { BKE_libblock_relink_ex(bmain, other_id, &reference_key->id, &local_key->id, - ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); + ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT); } } + if (other_id != id) { + other_id->lib = reference_id->lib; + } } FOREACH_MAIN_ID_END; } @@ -598,26 +637,22 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data if (ELEM(to_id, NULL, id_owner)) { continue; } - if (!ID_IS_OVERRIDE_LIBRARY(to_id) || ID_IS_LINKED(to_id)) { + if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id_owner->lib)) { continue; } - /* Do not tag 'virtual' overrides (shape keys here, as we already rejected embedded case - * above). */ - if (ID_IS_OVERRIDE_LIBRARY_REAL(to_id)) { - Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib; - ID *to_id_reference = lib_override_get(bmain, to_id)->reference; - if (to_id_reference->lib != reference_lib) { - /* We do not override data-blocks from other libraries, nor do we process them. */ - continue; - } + Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib; + ID *to_id_reference = lib_override_get(bmain, to_id)->reference; + if (to_id_reference->lib != reference_lib) { + /* We do not override data-blocks from other libraries, nor do we process them. */ + continue; + } - if (to_id_reference->tag & LIB_TAG_MISSING) { - to_id->tag |= missing_tag; - } - else { - to_id->tag |= tag; - } + if (to_id_reference->tag & LIB_TAG_MISSING) { + to_id->tag |= missing_tag; + } + else { + to_id->tag |= tag; } /* Recursively process the dependencies. */ @@ -631,7 +666,7 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data static void lib_override_local_group_tag(LibOverrideGroupTagData *data) { ID *id_root = data->id_root; - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root) && !ID_IS_LINKED(id_root)); + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -656,7 +691,7 @@ static bool lib_override_library_create_do(Main *bmain, ID *id_root) BKE_main_relations_free(bmain); - return BKE_lib_override_library_create_from_tag(bmain); + return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false); } static void lib_override_library_create_post_process(Main *bmain, @@ -667,6 +702,9 @@ static void lib_override_library_create_post_process(Main *bmain, Collection *residual_storage, const bool is_resync) { + /* NOTE: We only care about local IDs here, if a linked object is not instantiated in any way we + * do not do anything about it. */ + BKE_main_collection_sync(bmain); /* We create a set of all objects referenced into the scene by its hierarchy of collections. @@ -676,7 +714,7 @@ static void lib_override_library_create_post_process(Main *bmain, /* Instantiating the root collection or object should never be needed in resync case, since the * old override would be remapped to the new one. */ - if (!is_resync && id_root != NULL && id_root->newid != NULL) { + if (!is_resync && id_root != NULL && id_root->newid != NULL && !ID_IS_LINKED(id_root->newid)) { switch (GS(id_root->name)) { case ID_GR: { Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? @@ -721,56 +759,58 @@ static void lib_override_library_create_post_process(Main *bmain, Collection *default_instantiating_collection = residual_storage; LISTBASE_FOREACH (Object *, ob, &bmain->objects) { Object *ob_new = (Object *)ob->id.newid; - if (ob_new != NULL) { - BLI_assert(ob_new->id.override_library != NULL && - ob_new->id.override_library->reference == &ob->id); - - if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) { - if (id_root != NULL && default_instantiating_collection == NULL) { - ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root; - switch (GS(id_ref->name)) { - case ID_GR: { - /* Adding the object to a specific collection outside of the root overridden one is a - * fairly bad idea (it breaks the override hierarchy concept). But there is no other - * way to do this currently (we cannot add new collections to overridden root one, - * this is not currently supported). - * Since that will be fairly annoying and noisy, only do that in case the override - * object is not part of any existing collection (i.e. its user count is 0). In - * practice this should never happen I think. */ - if (ID_REAL_USERS(ob_new) != 0) { - continue; - } - default_instantiating_collection = BKE_collection_add( - bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); - /* Hide the collection from viewport and render. */ - default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; - break; + if (ob_new == NULL || ob_new->id.lib != NULL) { + continue; + } + + BLI_assert(ob_new->id.override_library != NULL && + ob_new->id.override_library->reference == &ob->id); + + if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) { + if (id_root != NULL && default_instantiating_collection == NULL) { + ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root; + switch (GS(id_ref->name)) { + case ID_GR: { + /* Adding the object to a specific collection outside of the root overridden one is a + * fairly bad idea (it breaks the override hierarchy concept). But there is no other + * way to do this currently (we cannot add new collections to overridden root one, + * this is not currently supported). + * Since that will be fairly annoying and noisy, only do that in case the override + * object is not part of any existing collection (i.e. its user count is 0). In + * practice this should never happen I think. */ + if (ID_REAL_USERS(ob_new) != 0) { + continue; } - case ID_OB: { - /* Add the other objects to one of the collections instantiating the - * root object, or scene's master collection if none found. */ - Object *ob_ref = (Object *)id_ref; - LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { - if (BKE_collection_has_object(collection, ob_ref) && - BKE_view_layer_has_collection(view_layer, collection) && - !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { - default_instantiating_collection = collection; - } + default_instantiating_collection = BKE_collection_add( + bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); + /* Hide the collection from viewport and render. */ + default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | + COLLECTION_RESTRICT_RENDER; + break; + } + case ID_OB: { + /* Add the other objects to one of the collections instantiating the + * root object, or scene's master collection if none found. */ + Object *ob_ref = (Object *)id_ref; + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (BKE_collection_has_object(collection, ob_ref) && + BKE_view_layer_has_collection(view_layer, collection) && + !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { + default_instantiating_collection = collection; } - break; } - default: - BLI_assert(0); + break; } + default: + BLI_assert(0); } - if (default_instantiating_collection == NULL) { - default_instantiating_collection = scene->master_collection; - } - - BKE_collection_object_add(bmain, default_instantiating_collection, ob_new); - DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } + if (default_instantiating_collection == NULL) { + default_instantiating_collection = scene->master_collection; + } + + BKE_collection_object_add(bmain, default_instantiating_collection, ob_new); + DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } } @@ -890,7 +930,6 @@ bool BKE_lib_override_library_resync(Main *bmain, ReportList *reports) { BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); - BLI_assert(!ID_IS_LINKED(id_root)); ID *id_root_reference = id_root->override_library->reference; @@ -924,12 +963,35 @@ bool BKE_lib_override_library_resync(Main *bmain, id->tag |= LIB_TAG_MISSING; } - if (id->tag & LIB_TAG_DOIT && !ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + if (id->tag & LIB_TAG_DOIT && (id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) { /* While this should not happen in typical cases (and won't be properly supported here), user * is free to do all kind of very bad things, including having different local overrides of a * same linked ID in a same hierarchy. */ - if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) { - BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id); + IDOverrideLibrary *id_override_library = lib_override_get(bmain, id); + ID *reference_id = id_override_library->reference; + if (GS(reference_id->name) != GS(id->name)) { + switch (GS(id->name)) { + case ID_KE: + reference_id = (ID *)BKE_key_from_id(reference_id); + break; + case ID_GR: + BLI_assert(GS(reference_id->name) == ID_SCE); + reference_id = (ID *)((Scene *)reference_id)->master_collection; + break; + case ID_NT: + reference_id = (ID *)ntreeFromID(id); + break; + default: + break; + } + } + BLI_assert(GS(reference_id->name) == GS(id->name)); + + if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) { + BLI_ghash_insert(linkedref_to_old_override, reference_id, id); + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + continue; + } if ((id->override_library->reference->tag & LIB_TAG_DOIT) == 0) { /* We have an override, but now it does not seem to be necessary to override that ID * anymore. Check if there are some actual overrides from the user, otherwise assume @@ -968,7 +1030,8 @@ bool BKE_lib_override_library_resync(Main *bmain, /* Note that this call also remaps all pointers of tagged IDs from old override IDs to new * override IDs (including within the old overrides themselves, since those are tagged too * above). */ - const bool success = BKE_lib_override_library_create_from_tag(bmain); + const bool success = BKE_lib_override_library_create_from_tag( + bmain, id_root_reference->lib, true); if (!success) { return success; @@ -977,55 +1040,100 @@ bool BKE_lib_override_library_resync(Main *bmain, ListBase *lb; FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { - if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { ID *id_override_new = id->newid; ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0); + /* We need to 'move back' newly created override into its proper library (since it was + * duplicated from the reference ID with 'no main' option, it should currently be the same + * as the reference ID one). */ + BLI_assert(/*id_override_new->lib == NULL || */ id_override_new->lib == id->lib); + BLI_assert(id_override_old == NULL || id_override_old->lib == id_root->lib); + id_override_new->lib = id_root->lib; + if (id_override_old != NULL) { /* Swap the names between old override ID and new one. */ char id_name_buf[MAX_ID_NAME]; memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf)); memcpy(id_override_old->name, id_override_new->name, sizeof(id_override_old->name)); memcpy(id_override_new->name, id_name_buf, sizeof(id_override_new->name)); - /* Note that this is a very efficient way to keep BMain IDs ordered as expected after - * swapping their names. - * However, one has to be very careful with this when iterating over the listbase at the - * same time. Here it works because we only execute this code when we are in the linked - * IDs, which are always *after* all local ones, and we only affect local IDs. */ - BLI_listbase_swaplinks(lb, id_override_old, id_override_new); - - /* Remap the whole local IDs to use the new override. */ - BKE_libblock_remap( - bmain, id_override_old, id_override_new, ID_REMAP_SKIP_INDIRECT_USAGE); - - /* Copy over overrides rules from old override ID to new one. */ - BLI_duplicatelist(&id_override_new->override_library->properties, - &id_override_old->override_library->properties); - for (IDOverrideLibraryProperty * - op_new = id_override_new->override_library->properties.first, - *op_old = id_override_old->override_library->properties.first; - op_new; - op_new = op_new->next, op_old = op_old->next) { - lib_override_library_property_copy(op_new, op_old); + + BLI_insertlinkreplace(lb, id_override_old, id_override_new); + id_override_old->tag |= LIB_TAG_NO_MAIN; + id_override_new->tag &= ~LIB_TAG_NO_MAIN; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)); + + /* Copy over overrides rules from old override ID to new one. */ + BLI_duplicatelist(&id_override_new->override_library->properties, + &id_override_old->override_library->properties); + IDOverrideLibraryProperty *op_new = + id_override_new->override_library->properties.first; + IDOverrideLibraryProperty *op_old = + id_override_old->override_library->properties.first; + for (; op_new; op_new = op_new->next, op_old = op_old->next) { + lib_override_library_property_copy(op_new, op_old); + } } } + else { + /* Add to proper main list, ensure unique name for local ID, sort, and clear relevant + * tags. */ + BKE_libblock_management_main_add(bmain, id_override_new); + } } } FOREACH_MAIN_LISTBASE_ID_END; } FOREACH_MAIN_LISTBASE_END; + /* We need to remap old to new override usages in a separate loop, after all new overrides have + * been added to Main. */ + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { + ID *id_override_new = id->newid; + ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + + if (id_override_old != NULL) { + /* Remap all IDs to use the new override. */ + BKE_libblock_remap(bmain, id_override_old, id_override_new, 0); + /* Remap no-main override IDs we just created too. */ + GHashIterator linkedref_to_old_override_iter; + GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) { + ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter); + if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) { + BKE_libblock_relink_ex(bmain, + id_override_old_iter, + id_override_old, + id_override_new, + ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); + } + } + } + } + } + FOREACH_MAIN_ID_END; + + BKE_main_collection_sync(bmain); + /* We need to apply override rules in a separate loop, after all ID pointers have been properly * remapped, and all new local override IDs have gotten their proper original names, otherwise * override operations based on those ID names would fail. */ FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { ID *id_override_new = id->newid; + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) { + continue; + } ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); - if (id_override_old != NULL) { + if (id_override_old == NULL) { + continue; + } + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)) { /* Apply rules on new override ID using old one as 'source' data. */ /* Note that since we already remapped ID pointers in old override IDs to new ones, we * can also apply ID pointer override rules safely here. */ @@ -1059,30 +1167,44 @@ bool BKE_lib_override_library_resync(Main *bmain, RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS : RNA_OVERRIDE_APPLY_FLAG_NOP); } + + /* Once overrides have been properly 'transferred' from old to new ID, we can clear ID usages + * of the old one. + * This is necessary in case said old ID is not in Main anymore. */ + BKE_libblock_relink_ex(bmain, + id_override_old, + NULL, + NULL, + ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); + id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT; } } FOREACH_MAIN_ID_END; /* Delete old override IDs. - * Note that we have to use tagged group deletion here, since ID deletion also uses LIB_TAG_DOIT. - * This improves performances anyway, so everything is fine. */ + * Note that we have to use tagged group deletion here, since ID deletion also uses + * LIB_TAG_DOIT. This improves performances anyway, so everything is fine. */ int user_edited_overrides_deletion_count = 0; FOREACH_MAIN_ID_BEGIN (bmain, id) { if (id->tag & LIB_TAG_DOIT) { - /* Note that this works because linked IDs are always after local ones (including overrides), - * so we will only ever tag an old override ID after we have already checked it in this loop, - * hence we cannot untag it later. */ - if (id->newid != NULL && ID_IS_LINKED(id)) { + /* Note that this works because linked IDs are always after local ones (including + * overrides), so we will only ever tag an old override ID after we have already checked it + * in this loop, hence we cannot untag it later. */ + if (id->newid != NULL && id->lib == id_root_reference->lib) { ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); if (id_override_old != NULL) { id->newid->tag &= ~LIB_TAG_DOIT; id_override_old->tag |= LIB_TAG_DOIT; + if (id_override_old->tag & LIB_TAG_NO_MAIN) { + BKE_id_free(bmain, id_override_old); + } } } id->tag &= ~LIB_TAG_DOIT; } - /* Also deal with old overrides that went missing in new linked data. */ + /* Also deal with old overrides that went missing in new linked data - only for real local + * overrides for now, not those who are linked. */ else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) { BLI_assert(ID_IS_OVERRIDE_LIBRARY(id)); if (!BKE_lib_override_library_is_user_edited(id)) { @@ -1113,6 +1235,10 @@ bool BKE_lib_override_library_resync(Main *bmain, } } FOREACH_MAIN_ID_END; + + /* Cleanup, many pointers in this GHash are already invalid now. */ + BLI_ghash_free(linkedref_to_old_override, NULL, NULL); + BKE_id_multi_tagged_delete(bmain); /* At this point, `id_root` has very likely been deleted, we need to update it to its new @@ -1145,14 +1271,56 @@ bool BKE_lib_override_library_resync(Main *bmain, } /* Cleanup. */ - BLI_ghash_free(linkedref_to_old_override, NULL, NULL); - BKE_main_id_clear_newpoins(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* That one should not be needed in fact. */ return success; } +static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) +{ + ID *id_owner = cb_data->id_owner; + ID *id = *cb_data->id_pointer; + if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) { + const int owner_library_indirect_level = id_owner->lib != NULL ? id_owner->lib->temp_index : 0; + if (owner_library_indirect_level >= id->lib->temp_index) { + id->lib->temp_index = owner_library_indirect_level + 1; + *(bool *)cb_data->user_data = true; + } + } + return IDWALK_RET_NOP; +} + +/** Define the `temp_index` of libraries from their highest level of indirect usage. + * + * E.g. if lib_a uses lib_b, lib_c and lib_d, and lib_b also uses lib_d, then lib_a has an index of + * 1, lib_b and lib_c an index of 2, and lib_d an index of 3. */ +static int lib_override_libraries_index_define(Main *bmain) +{ + LISTBASE_FOREACH (Library *, library, &bmain->libraries) { + /* index 0 is reserved for local data. */ + library->temp_index = 1; + } + bool do_continue = true; + while (do_continue) { + do_continue = false; + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + BKE_library_foreach_ID_link( + bmain, id, lib_override_sort_libraries_func, &do_continue, IDWALK_READONLY); + } + FOREACH_MAIN_ID_END; + } + + int library_indirect_level_max = 0; + LISTBASE_FOREACH (Library *, library, &bmain->libraries) { + if (library->temp_index > library_indirect_level_max) { + library_indirect_level_max = library->temp_index; + } + } + return library_indirect_level_max; +} + /** * Detect and handle required resync of overrides data, when relations between reference linked IDs * have changed. @@ -1200,7 +1368,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, * those used by current existing overrides. */ ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) { + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { continue; } if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) { @@ -1222,12 +1390,12 @@ void BKE_lib_override_library_main_resync(Main *bmain, /* Now check existing overrides, those needing resync will be the one either already tagged as * such, or the one using linked data that is now tagged as needing override. */ FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) { + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { continue; } if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) { - CLOG_INFO(&LOG, 4, "ID %s was already tagged as needing resync", id->name); + CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib); continue; } @@ -1242,14 +1410,16 @@ void BKE_lib_override_library_main_resync(Main *bmain, ID *id_to = *entry_item->id_pointer.to; /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */ - if (ID_IS_LINKED(id_to) && (id_to->tag & LIB_TAG_DOIT) != 0) { + if (ID_IS_LINKED(id_to) && (id_to->lib != id->lib) && (id_to->tag & LIB_TAG_DOIT) != 0) { id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; CLOG_INFO(&LOG, 3, - "ID %s now tagged as needing resync because they use linked %s that now needs " - "to be overridden", + "ID %s (%p) now tagged as needing resync because they use linked %s (%p) that " + "now needs to be overridden", id->name, - id_to->name); + id->lib, + id_to->name, + id_to->lib); break; } } @@ -1259,6 +1429,8 @@ void BKE_lib_override_library_main_resync(Main *bmain, BKE_main_relations_free(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + int library_indirect_level = lib_override_libraries_index_define(bmain); + /* And do the actual resync for all IDs detected as needing it. * NOTE: Since this changes `bmain` (adding **and** removing IDs), we cannot use * `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */ @@ -1268,13 +1440,12 @@ void BKE_lib_override_library_main_resync(Main *bmain, do_continue = false; FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { - if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) { + if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 || + (ID_IS_LINKED(id) && id->lib->temp_index < library_indirect_level) || + (!ID_IS_LINKED(id) && library_indirect_level != 0)) { continue; } BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); - if (ID_IS_LINKED(id)) { - continue; - } do_continue = true; /* In complex non-supported cases, with several different override hierarchies sharing @@ -1284,7 +1455,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, * This can lead to infinite loop here, at least avoid this. */ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; - CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name); + CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, id->lib); const bool success = BKE_lib_override_library_resync( bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports); CLOG_INFO(&LOG, 2, "\tSuccess: %d", success); @@ -1296,6 +1467,13 @@ void BKE_lib_override_library_main_resync(Main *bmain, } } FOREACH_MAIN_LISTBASE_END; + + if (!do_continue && library_indirect_level != 0) { + /* We are done with overrides from that level of indirect linking, we can keep going with + * those 'less' indirectly linked now. */ + library_indirect_level--; + do_continue = true; + } } /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 8bf9afafa1b..dd262f78f6b 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -366,7 +366,7 @@ typedef struct Library { struct PackedFile *packedfile; - /* Temp data needed by read/write code. */ + /* Temp data needed by read/write code, and liboverride recursive resync. */ int temp_index; /** See BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION, needed for do_versions. */ short versionfile, subversionfile; -- cgit v1.2.3 From 6b00df110552c3b54e6bfcc1247886f015d83c0c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 27 May 2021 01:30:52 +1000 Subject: Cleanup: shadow warning Move reproject_type into an extern, to avoid declaring multiple times. --- .../editors/gpencil/gpencil_bake_animation.c | 31 +++++++++++++++++++--- source/blender/editors/gpencil/gpencil_intern.h | 20 +------------- source/blender/editors/gpencil/gpencil_mesh.c | 7 ++++- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c index 6063594826e..30ebc9189c5 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.c +++ b/source/blender/editors/gpencil/gpencil_bake_animation.c @@ -59,6 +59,26 @@ #include "gpencil_intern.h" +const EnumPropertyItem rna_gpencil_reproject_type_items[] = { + {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, + {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, + {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, + {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, + {GP_REPROJECT_VIEW, + "VIEW", + 0, + "View", + "Reproject the strokes to end up on the same plane, as if drawn from the current " + "viewpoint " + "using 'Cursor' Stroke Placement"}, + {GP_REPROJECT_CURSOR, + "CURSOR", + 0, + "Cursor", + "Reproject the strokes using the orientation of 3D cursor"}, + {0, NULL, 0, NULL, NULL}, +}; + /* Check frame_end is always > start frame! */ static void gpencil_bake_set_frame_end(struct Main *UNUSED(main), struct Scene *UNUSED(scene), @@ -314,8 +334,8 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op gps->mat_nr = BKE_gpencil_object_material_index_get(ob_gpencil, ma_src); /* Update point location to new object space. */ - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; + for (int j = 0; j < gps->totpoints; j++) { + bGPDspoint *pt = &gps->points[j]; mul_m4_v3(matrix, &pt->x); mul_m4_v3(invmat, &pt->x); } @@ -419,5 +439,10 @@ void GPENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot) RNA_def_int( ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); - RNA_def_enum(ot->srna, "project_type", reproject_type, GP_REPROJECT_KEEP, "Projection Type", ""); + RNA_def_enum(ot->srna, + "project_type", + rna_gpencil_reproject_type_items, + GP_REPROJECT_KEEP, + "Projection Type", + ""); } diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 665401e8fee..0e8fdc3c375 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -752,24 +752,6 @@ struct GP_EditableStrokes_Iter { (void)0 /* Reused items for bake operators. */ -static const EnumPropertyItem reproject_type[] = { - {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""}, - {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"}, - {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"}, - {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"}, - {GP_REPROJECT_VIEW, - "VIEW", - 0, - "View", - "Reproject the strokes to end up on the same plane, as if drawn from the current " - "viewpoint " - "using 'Cursor' Stroke Placement"}, - {GP_REPROJECT_CURSOR, - "CURSOR", - 0, - "Cursor", - "Reproject the strokes using the orientation of 3D cursor"}, - {0, NULL, 0, NULL, NULL}, -}; +extern const EnumPropertyItem rna_gpencil_reproject_type_items[]; /* ****************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index e85221fba8c..55468dffab0 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -472,5 +472,10 @@ void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot) RNA_def_int( ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000); - RNA_def_enum(ot->srna, "project_type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", ""); + RNA_def_enum(ot->srna, + "project_type", + rna_gpencil_reproject_type_items, + GP_REPROJECT_VIEW, + "Projection Type", + ""); } -- cgit v1.2.3 From df32b50344a012ecfb3749c577b1e9e1867dedb3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 26 May 2021 16:06:02 +1000 Subject: Fix T88111: Skin modifier assets within invalid face normals The skin modifier was moving vertices without updating normals for the connected faces, this happened when smoothing and welding vertices. Reviewed By: mont29 Ref D11397 --- source/blender/modifiers/intern/MOD_skin.c | 63 +++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index e2d18cf1790..58d70ef3a4a 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -90,6 +90,46 @@ #include "bmesh.h" +/* -------------------------------------------------------------------- */ +/** \name Generic BMesh Utilities + * \{ */ + +static void vert_face_normal_mark_set(BMVert *v) +{ + BMIter iter; + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + f->no[0] = FLT_MAX; + } +} + +static void vert_face_normal_mark_update(BMVert *v) +{ + BMIter iter; + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + if (f->no[0] == FLT_MAX) { + BM_face_normal_update(f); + } + } +} + +/** + * Recalculate the normals of all faces connected to `verts`. + */ +static void vert_array_face_normal_update(BMVert **verts, int verts_len) +{ + for (int i = 0; i < verts_len; i++) { + vert_face_normal_mark_set(verts[i]); + } + + for (int i = 0; i < verts_len; i++) { + vert_face_normal_mark_update(verts[i]); + } +} + +/** \} */ + typedef struct { float mat[3][3]; /* Vert that edge is pointing away from, no relation to @@ -1352,13 +1392,25 @@ static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_f split_face = collapse_face_corners(bm, split_face, 4, vert_buf); } - /* Done with dynamic array, split_face must now be a quad */ - BLI_array_free(vert_buf); + /* `split_face` should now be a quad. */ BLI_assert(split_face->len == 4); + + /* Account for the highly unlikely case that it's not a quad. */ if (split_face->len != 4) { + /* Reuse `vert_buf` for updating normals. */ + BLI_array_clear(vert_buf); + BLI_array_grow_items(vert_buf, split_face->len); + + BM_iter_as_array(bm, BM_FACES_OF_VERT, split_face, (void **)vert_buf, split_face->len); + + vert_array_face_normal_update(vert_buf, split_face->len); + BLI_array_free(vert_buf); return; } + /* Done with dynamic array. */ + BLI_array_free(vert_buf); + /* Get split face's verts */ // BM_iter_as_array(bm, BM_VERTS_OF_FACE, split_face, (void **)verts, 4); BM_face_as_array_vert_quad(split_face, verts); @@ -1373,6 +1425,8 @@ static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_f } BMO_op_exec(bm, &op); BMO_op_finish(bm, &op); + + vert_array_face_normal_update(frame->verts, 4); } /* If the frame has some vertices that are inside the hull (detached) @@ -1731,6 +1785,11 @@ static void skin_smooth_hulls(BMesh *bm, /* Done with original coordinates */ BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey); + + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BM_face_normal_update(f); + } } /* Returns true if all hulls are successfully built, false otherwise */ -- cgit v1.2.3 From de3d54eb9a4fa9ddb1046190ba13daacc4f1dea1 Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 26 May 2021 18:23:25 +0200 Subject: GPencil: Cleanup - Conform with RNA naming scheme The newly added `disable_masks_viewlayer` RNA property did not conform with the RNA naming scheme. This renames it to `use_viewlayer_masks`. --- release/scripts/startup/bl_ui/properties_grease_pencil_common.py | 4 ++-- source/blender/makesrna/intern/rna_gpencil.c | 6 +++--- 2 files changed, 5 insertions(+), 5 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 0111bdea8b1..b6d70049bb9 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -856,11 +856,11 @@ class GreasePencilLayerRelationsPanel: col = layout.row(align=True) col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer") - + col = layout.row(align=True) # Only enable this property when a view layer is selected. col.enabled = bool(gpl.viewlayer_render) - col.prop(gpl, "disable_masks_viewlayer") + col.prop(gpl, "use_viewlayer_masks") class GreasePencilLayerDisplayPanel: diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 01f564cb272..5f865f9d4cd 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -2114,10 +2114,10 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) "ViewLayer", "Only include Layer in this View Layer render output (leave blank to include always)"); - prop = RNA_def_property(srna, "disable_masks_viewlayer", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER); + prop = RNA_def_property(srna, "use_viewlayer_masks", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER); RNA_def_property_ui_text( - prop, "Disable Masks in Render", "Exclude the mask layers when rendering the viewlayer"); + prop, "Use Masks in Render", "Include the mask layers when rendering the viewlayer"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* blend mode */ -- cgit v1.2.3 From f12e2ec088655a9a159dc98b01fca4f633dae60c Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 26 May 2021 17:11:45 +0200 Subject: Fix buildbot CUDA/OptiX warnings on macOS Explicitly disable these, rather than relying on them not being found. Also, don't duplicates the architectures list. --- build_files/cmake/config/blender_release.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake index d4fd3779665..75aafd45c82 100644 --- a/build_files/cmake/config/blender_release.cmake +++ b/build_files/cmake/config/blender_release.cmake @@ -56,10 +56,6 @@ set(WITH_TBB ON CACHE BOOL "" FORCE) set(WITH_USD ON CACHE BOOL "" FORCE) set(WITH_MEM_JEMALLOC ON CACHE BOOL "" FORCE) -set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE) -set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE) -set(CYCLES_CUDA_BINARIES_ARCH sm_30;sm_35;sm_37;sm_50;sm_52;sm_60;sm_61;sm_70;sm_75;sm_86;compute_75 CACHE STRING "" FORCE) -set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE) # platform dependent options if(APPLE) @@ -80,4 +76,8 @@ if(UNIX AND NOT APPLE) endif() if(NOT APPLE) set(WITH_XR_OPENXR ON CACHE BOOL "" FORCE) + + set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE) + set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE) + set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE) endif() -- cgit v1.2.3 From c07c7957c6b4780b643e0e056a78a56b3e08f51b Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 26 May 2021 18:22:43 +0200 Subject: Revert "Cycles: optimize ensure_valid_reflection(), reduces render time by about 1%" Both before and after can have artifacts with some normal maps, but this seems to give worse artifacts on average which are not worth the minor performance increase. This reverts commit 21bc1a99baa765d81c3203fd2e451681b8a7fd55. Ref T88368, D10084 --- intern/cycles/kernel/kernel_montecarlo.h | 117 +++++++++++++++++++++++++------ intern/cycles/kernel/shaders/stdcycles.h | 69 +++++++++++++----- 2 files changed, 150 insertions(+), 36 deletions(-) diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/kernel_montecarlo.h index ba25c0e24e4..ce37bd0b15e 100644 --- a/intern/cycles/kernel/kernel_montecarlo.h +++ b/intern/cycles/kernel/kernel_montecarlo.h @@ -195,31 +195,108 @@ ccl_device float2 regular_polygon_sample(float corners, float rotation, float u, ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N) { - float3 R; - float NI = dot(N, I); - float NgR, threshold; - - /* Check if the incident ray is coming from behind normal N. */ - if (NI > 0) { - /* Normal reflection */ - R = (2 * NI) * N - I; - NgR = dot(Ng, R); - - /* Reflection rays may always be at least as shallow as the incoming ray. */ - threshold = min(0.9f * dot(Ng, I), 0.01f); - if (NgR >= threshold) { - return N; + float3 R = 2 * dot(N, I) * N - I; + + /* Reflection rays may always be at least as shallow as the incoming ray. */ + float threshold = min(0.9f * dot(Ng, I), 0.01f); + if (dot(Ng, R) >= threshold) { + return N; + } + + /* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane. + * The X axis is found by normalizing the component of N that's orthogonal to Ng. + * The Y axis isn't actually needed. + */ + float NdotNg = dot(N, Ng); + float3 X = normalize(N - NdotNg * Ng); + + /* Keep math expressions. */ + /* clang-format off */ + /* Calculate N.z and N.x in the local coordinate system. + * + * The goal of this computation is to find a N' that is rotated towards Ng just enough + * to lift R' above the threshold (here called t), therefore dot(R', Ng) = t. + * + * According to the standard reflection equation, + * this means that we want dot(2*dot(N', I)*N' - I, Ng) = t. + * + * Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get + * 2*dot(N', I)*N'.z - I.z = t. + * + * The rotation is simple to express in the coordinate system we formed - + * since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane, + * so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z . + * + * Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2). + * + * With these simplifications, + * we get the final equation 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t. + * + * The only unknown here is N'.z, so we can solve for that. + * + * The equation has four solutions in general: + * + * N'.z = +-sqrt(0.5*(+-sqrt(I.x^2*(I.x^2 + I.z^2 - t^2)) + t*I.z + I.x^2 + I.z^2)/(I.x^2 + I.z^2)) + * We can simplify this expression a bit by grouping terms: + * + * a = I.x^2 + I.z^2 + * b = sqrt(I.x^2 * (a - t^2)) + * c = I.z*t + a + * N'.z = +-sqrt(0.5*(+-b + c)/a) + * + * Two solutions can immediately be discarded because they're negative so N' would lie in the + * lower hemisphere. + */ + /* clang-format on */ + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = safe_sqrtf(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + /* Evaluate both solutions. + * In many cases one can be immediately discarded (if N'.z would be imaginary or larger than + * one), so check for that first. If no option is viable (might happen in extreme cases like N + * being in the wrong hemisphere), give up and return Ng. */ + float fac = 0.5f / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + bool valid1 = (N1_z2 > 1e-5f) && (N1_z2 <= (1.0f + 1e-5f)); + bool valid2 = (N2_z2 > 1e-5f) && (N2_z2 <= (1.0f + 1e-5f)); + + float2 N_new; + if (valid1 && valid2) { + /* If both are possible, do the expensive reflection-based check. */ + float2 N1 = make_float2(safe_sqrtf(1.0f - N1_z2), safe_sqrtf(N1_z2)); + float2 N2 = make_float2(safe_sqrtf(1.0f - N2_z2), safe_sqrtf(N2_z2)); + + float R1 = 2 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; + float R2 = 2 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; + + valid1 = (R1 >= 1e-5f); + valid2 = (R2 >= 1e-5f); + if (valid1 && valid2) { + /* If both solutions are valid, return the one with the shallower reflection since it will be + * closer to the input (if the original reflection wasn't shallow, we would not be in this + * part of the function). */ + N_new = (R1 < R2) ? N1 : N2; } + else { + /* If only one reflection is valid (= positive), pick that one. */ + N_new = (R1 > R2) ? N1 : N2; + } + } + else if (valid1 || valid2) { + /* Only one solution passes the N'.z criterium, so pick that one. */ + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new = make_float2(safe_sqrtf(1.0f - Nz2), safe_sqrtf(Nz2)); } else { - /* Bad incident */ - R = -I; - NgR = dot(Ng, R); - threshold = 0.01f; + return Ng; } - R = R + Ng * (threshold - NgR); /* Lift the reflection above the threshold. */ - return normalize(I * len(R) + R * len(I)); /* Find a bisector. */ + return N_new.x * X + N_new.y * Ng; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/shaders/stdcycles.h b/intern/cycles/kernel/shaders/stdcycles.h index af7b645d9a2..dd604da68ce 100644 --- a/intern/cycles/kernel/shaders/stdcycles.h +++ b/intern/cycles/kernel/shaders/stdcycles.h @@ -84,30 +84,67 @@ closure color principled_hair(normal N, closure color henyey_greenstein(float g) BUILTIN; closure color absorption() BUILTIN; -normal ensure_valid_reflection(normal Ng, normal I, normal N) +normal ensure_valid_reflection(normal Ng, vector I, normal N) { /* The implementation here mirrors the one in kernel_montecarlo.h, * check there for an explanation of the algorithm. */ - vector R; - float NI = dot(N, I); - float NgR, threshold; - - if (NI > 0) { - R = (2 * NI) * N - I; - NgR = dot(Ng, R); - threshold = min(0.9 * dot(Ng, I), 0.01); - if (NgR >= threshold) { - return N; + + float sqr(float x) + { + return x * x; + } + + vector R = 2 * dot(N, I) * N - I; + + float threshold = min(0.9 * dot(Ng, I), 0.01); + if (dot(Ng, R) >= threshold) { + return N; + } + + float NdotNg = dot(N, Ng); + vector X = normalize(N - NdotNg * Ng); + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = sqrt(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + float fac = 0.5 / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + int valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5)); + int valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5)); + + float N_new_x, N_new_z; + if (valid1 && valid2) { + float N1_x = sqrt(1.0 - N1_z2), N1_z = sqrt(N1_z2); + float N2_x = sqrt(1.0 - N2_z2), N2_z = sqrt(N2_z2); + + float R1 = 2 * (N1_x * Ix + N1_z * Iz) * N1_z - Iz; + float R2 = 2 * (N2_x * Ix + N2_z * Iz) * N2_z - Iz; + + valid1 = (R1 >= 1e-5); + valid2 = (R2 >= 1e-5); + if (valid1 && valid2) { + N_new_x = (R1 < R2) ? N1_x : N2_x; + N_new_z = (R1 < R2) ? N1_z : N2_z; + } + else { + N_new_x = (R1 > R2) ? N1_x : N2_x; + N_new_z = (R1 > R2) ? N1_z : N2_z; } } + else if (valid1 || valid2) { + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new_x = sqrt(1.0 - Nz2); + N_new_z = sqrt(Nz2); + } else { - R = -I; - NgR = dot(Ng, R); - threshold = 0.01; + return Ng; } - R = R + Ng * (threshold - NgR); - return normalize(I * length(R) + R * length(I)); + return N_new_x * X + N_new_z * Ng; } #endif /* CCL_STDOSL_H */ -- cgit v1.2.3 From 7438f0c6c0513dec73c0b91e62c5fc52bbcde3dd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 27 May 2021 02:23:19 +1000 Subject: Cleanup: array-parameter warning with GCC 11 Pass the string size as this is less error prone in general. --- source/blender/editors/transform/transform_mode.c | 36 ++++++++-------------- source/blender/editors/transform/transform_mode.h | 4 +-- .../transform/transform_mode_edge_rotate_normal.c | 2 +- .../editors/transform/transform_mode_resize.c | 4 +-- .../editors/transform/transform_mode_rotate.c | 2 +- .../editors/transform/transform_mode_skin_resize.c | 2 +- 6 files changed, 20 insertions(+), 30 deletions(-) diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index d61e7bb867c..35b5c6f7f5d 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -524,7 +524,7 @@ void constraintSizeLim(TransInfo *t, TransData *td) /** \name Transform (Rotation Utils) * \{ */ /* Used by Transform Rotation and Transform Normal Rotation */ -void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final) +void headerRotation(TransInfo *t, char *str, const int str_size, float final) { size_t ofs = 0; @@ -533,16 +533,12 @@ void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final) outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_("Rotation: %s %s %s"), - &c[0], - t->con.text, - t->proptext); + ofs += BLI_snprintf( + str + ofs, str_size - ofs, TIP_("Rotation: %s %s %s"), &c[0], t->con.text, t->proptext); } else { ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, + str_size - ofs, TIP_("Rotation: %.2f%s %s"), RAD2DEGF(final), t->con.text, @@ -550,8 +546,7 @@ void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final) } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( - str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf(str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } } @@ -811,7 +806,7 @@ void ElementRotation( /* -------------------------------------------------------------------- */ /** \name Transform (Resize Utils) * \{ */ -void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) +void headerResize(TransInfo *t, const float vec[3], char *str, const int str_size) { char tvec[NUM_STR_REP_LEN * 3]; size_t ofs = 0; @@ -827,16 +822,12 @@ void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) if (t->con.mode & CON_APPLY) { switch (t->num.idx_max) { case 0: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_("Scale: %s%s %s"), - &tvec[0], - t->con.text, - t->proptext); + ofs += BLI_snprintf( + str + ofs, str_size - ofs, TIP_("Scale: %s%s %s"), &tvec[0], t->con.text, t->proptext); break; case 1: ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, + str_size - ofs, TIP_("Scale: %s : %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], @@ -845,7 +836,7 @@ void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) break; case 2: ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, + str_size - ofs, TIP_("Scale: %s : %s : %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], @@ -858,7 +849,7 @@ void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) else { if (t->flag & T_2D_EDIT) { ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, + str_size - ofs, TIP_("Scale X: %s Y: %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], @@ -867,7 +858,7 @@ void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) } else { ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, + str_size - ofs, TIP_("Scale X: %s Y: %s Z: %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], @@ -878,8 +869,7 @@ void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( - str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf(str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } } diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index 106dc68c9ee..874a5ebc0a8 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -47,7 +47,7 @@ void protectedTransBits(short protectflag, float vec[3]); void protectedSizeBits(short protectflag, float size[3]); void constraintTransLim(TransInfo *t, TransData *td); void constraintSizeLim(TransInfo *t, TransData *td); -void headerRotation(TransInfo *t, char *str, float final); +void headerRotation(TransInfo *t, char *str, int str_len, float final); void ElementRotation_ex(TransInfo *t, TransDataContainer *tc, TransData *td, @@ -55,7 +55,7 @@ void ElementRotation_ex(TransInfo *t, const float *center); void ElementRotation( TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around); -void headerResize(TransInfo *t, const float vec[3], char *str); +void headerResize(TransInfo *t, const float vec[3], char *str, int str_len); void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]); short getAnimEdit_SnapMode(TransInfo *t); void doAnimEdit_SnapFrame( diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c index b7b3de69731..6f2bcc148ce 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c @@ -103,7 +103,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &angle); - headerRotation(t, str, angle); + headerRotation(t, str, sizeof(str), angle); axis_angle_normalized_to_mat3(mat, axis, angle); diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 0d7d0be3c0e..1d7d1369f29 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -113,10 +113,10 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) pvec[j++] = t->values_final[i]; } } - headerResize(t, pvec, str); + headerResize(t, pvec, str, sizeof(str)); } else { - headerResize(t, t->values_final, str); + headerResize(t, t->values_final, str, sizeof(str)); } copy_m3_m3(t->mat, mat); /* used in gizmo */ diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index 0fdbfb25989..8350e94e0e8 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -217,7 +217,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) t->values_final[0] = final; - headerRotation(t, str, final); + headerRotation(t, str, sizeof(str), final); const bool is_large_rotation = hasNumInput(&t->num); applyRotationValue(t, final, axis_final, is_large_rotation); diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c index 77e57484bef..75ad83b0787 100644 --- a/source/blender/editors/transform/transform_mode_skin_resize.c +++ b/source/blender/editors/transform/transform_mode_skin_resize.c @@ -64,7 +64,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) size_to_mat3(mat, t->values_final); - headerResize(t, t->values_final, str); + headerResize(t, t->values_final, str, sizeof(str)); FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransData *td = tc->data; -- cgit v1.2.3 From 2c607ec2f6b9df98da5150ca49e4405385dd4e27 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 26 May 2021 20:32:05 +0200 Subject: Revert "DrawManager: Use Compute Shader to Update Hair." This reverts commit 8f9599d17e80254928d2d72081a4c7e0dee64038. Mac seems to have an error with this change. ``` ERROR: /Users/blender/git/blender-vdev/blender.git/source/blender/draw/intern/draw_hair.c:115:44: error: use of undeclared identifier 'shader_src' ERROR: /Users/blender/git/blender-vdev/blender.git/source/blender/draw/intern/draw_hair.c:123:13: error: use of undeclared identifier 'shader_src' ERROR: make[2]: *** [source/blender/draw/CMakeFiles/bf_draw.dir/intern/draw_hair.c.o] Error 1 ERROR: make[1]: *** [source/blender/draw/CMakeFiles/bf_draw.dir/all] Error 2 ERROR: make: *** [all] Error 2 ``` --- source/blender/draw/CMakeLists.txt | 1 - source/blender/draw/intern/DRW_render.h | 7 -- source/blender/draw/intern/draw_cache_impl_hair.c | 3 +- source/blender/draw/intern/draw_hair.c | 135 ++++++--------------- source/blender/draw/intern/draw_manager.h | 13 -- source/blender/draw/intern/draw_manager_data.c | 36 ------ source/blender/draw/intern/draw_manager_exec.c | 10 -- .../draw/intern/shaders/common_hair_lib.glsl | 78 +----------- .../intern/shaders/common_hair_refine_comp.glsl | 24 ---- .../intern/shaders/common_hair_refine_vert.glsl | 45 ++++++- source/blender/gpu/GPU_capabilities.h | 2 - source/blender/gpu/intern/gpu_capabilities.cc | 10 -- .../blender/gpu/intern/gpu_capabilities_private.hh | 2 - source/blender/gpu/opengl/gl_backend.cc | 8 -- 14 files changed, 82 insertions(+), 292 deletions(-) delete mode 100644 source/blender/draw/intern/shaders/common_hair_refine_comp.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 95c0f5d300c..045adf4b380 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -321,7 +321,6 @@ data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC) data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC) -data_to_c_simple(intern/shaders/common_hair_refine_comp.glsl SRC) data_to_c_simple(intern/shaders/common_math_lib.glsl SRC) data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC) data_to_c_simple(intern/shaders/common_view_lib.glsl SRC) diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 5071658fd82..2545cfa65dc 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -438,10 +438,6 @@ void DRW_shgroup_call_range( void DRW_shgroup_call_instance_range( DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct); -void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, - int groups_x_len, - int groups_y_len, - int groups_z_len); void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count); void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count); void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count); @@ -579,9 +575,6 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, const char *name, const float (*value)[4], int arraysize); -void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, - const char *name, - struct GPUVertBuf *vertex_buffer); bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup); diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c index 6424b21666d..fd28ac00186 100644 --- a/source/blender/draw/intern/draw_cache_impl_hair.c +++ b/source/blender/draw/intern/draw_cache_impl_hair.c @@ -243,8 +243,7 @@ static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *c GPUVertFormat format = {0}; GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format_ex(&format, - GPU_USAGE_DEVICE_ONLY); + cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format(&format); /* Create a destination buffer for the transform feedback. Sized appropriately */ /* Those are points! not line segments. */ diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index 258777b34fb..bca227a24e2 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -36,28 +36,15 @@ #include "BKE_duplilist.h" #include "GPU_batch.h" -#include "GPU_capabilities.h" -#include "GPU_compute.h" #include "GPU_shader.h" -#include "GPU_texture.h" #include "GPU_vertex_buffer.h" #include "draw_hair_private.h" #ifndef __APPLE__ # define USE_TRANSFORM_FEEDBACK -# define USE_COMPUTE_SHADERS #endif -BLI_INLINE bool drw_hair_use_compute_shaders(void) -{ -#ifdef USE_COMPUTE_SHADERS - return GPU_compute_shader_support(); -#else - return false; -#endif -} - typedef enum ParticleRefineShader { PART_REFINE_CATMULL_ROM = 0, PART_REFINE_MAX_SHADER, @@ -84,7 +71,6 @@ static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in t extern char datatoc_common_hair_lib_glsl[]; extern char datatoc_common_hair_refine_vert_glsl[]; -extern char datatoc_common_hair_refine_comp_glsl[]; extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; static GPUShader *hair_refine_shader_get(ParticleRefineShader sh) @@ -93,26 +79,15 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader sh) return g_refine_shaders[sh]; } -#ifdef USE_COMPUTE_SHADERS - const bool do_compute = drw_hair_use_compute_shaders(); - if (do_compute) { - g_refine_shaders[sh] = GPU_shader_create_compute(datatoc_common_hair_refine_comp_glsl, - datatoc_common_hair_lib_glsl, - "#define HAIR_PHASE_SUBDIV\n", - __func__); - return g_refine_shaders[sh]; - } -#endif + char *vert_with_lib = BLI_string_joinN(datatoc_common_hair_lib_glsl, + datatoc_common_hair_refine_vert_glsl); #ifdef USE_TRANSFORM_FEEDBACK - char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); const char *var_names[1] = {"finalColor"}; g_refine_shaders[sh] = DRW_shader_create_with_transform_feedback( - shader_src, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); - + vert_with_lib, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); #else - g_refine_shaders[sh] = DRW_shader_create(shader_src, + g_refine_shaders[sh] = DRW_shader_create(vert_with_lib, NULL, datatoc_gpu_shader_3D_smooth_color_frag_glsl, "#define blender_srgb_to_framebuffer_space(a) a\n" @@ -120,14 +95,14 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader sh) "#define TF_WORKAROUND\n"); #endif - MEM_freeN(shader_src); + MEM_freeN(vert_with_lib); return g_refine_shaders[sh]; } void DRW_hair_init(void) { -#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) +#ifdef USE_TRANSFORM_FEEDBACK g_tf_pass = DRW_pass_create("Update Hair Pass", 0); #else g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR); @@ -150,67 +125,6 @@ void DRW_hair_init(void) } } -static void drw_hair_particle_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, - ParticleHairCache *cache, - const int subdiv) -{ - DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex); - DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex); - DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); - DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); -} - -static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, const int subdiv) -{ - const int strands_len = cache->strands_len; - const int final_points_len = cache->final[subdiv].strands_res * strands_len; - if (final_points_len > 0) { - GPUShader *shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); - DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); - drw_hair_particle_cache_shgrp_attach_resources(shgrp, cache, subdiv); - DRW_shgroup_vertex_buffer(shgrp, "hairPointOutputBuffer", cache->final[subdiv].proc_buf); - - const int max_strands_per_call = GPU_max_work_group_count(0); - int strands_start = 0; - while (strands_start < strands_len) { - int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); - DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); - DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); - DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); - strands_start += batch_strands_len; - } - } -} - -static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache *cache, - const int subdiv) -{ - const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; - if (final_points_len > 0) { - GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); - -#ifdef USE_TRANSFORM_FEEDBACK - DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( - tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); -#else - DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - - ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); - pr_call->next = g_tf_calls; - pr_call->vbo = cache->final[subdiv].proc_buf; - pr_call->shgrp = tf_shgrp; - pr_call->vert_len = final_points_len; - g_tf_calls = pr_call; - DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); - DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); - DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); -#endif - - drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); - DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); - } -} - static ParticleHairCache *drw_hair_particle_cache_get( Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res) { @@ -226,11 +140,32 @@ static ParticleHairCache *drw_hair_particle_cache_get( } if (update) { - if (drw_hair_use_compute_shaders()) { - drw_hair_particle_cache_update_compute(cache, subdiv); - } - else { - drw_hair_particle_cache_update_transform_feedback(cache, subdiv); + int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; + if (final_points_len > 0) { + GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); + +#ifdef USE_TRANSFORM_FEEDBACK + DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( + tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); +#else + DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); + + ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); + pr_call->next = g_tf_calls; + pr_call->vbo = cache->final[subdiv].proc_buf; + pr_call->shgrp = tf_shgrp; + pr_call->vert_len = final_points_len; + g_tf_calls = pr_call; + DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); + DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); + DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); +#endif + + DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", cache->point_tex); + DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", cache->strand_tex); + DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); + DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); + DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); } } return cache; @@ -432,11 +367,9 @@ void DRW_hair_update(void) MEM_freeN(data); GPU_framebuffer_free(fb); #else - /* Just render the pass when using compute shaders or transform feedback. */ + /* TODO(fclem): replace by compute shader. */ + /* Just render using transform feedback. */ DRW_draw_pass(g_tf_pass); - if (drw_hair_use_compute_shaders()) { - GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); - } #endif } diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index d4e22c83798..84bc0327aa2 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -187,10 +187,6 @@ typedef enum { DRW_CMD_DRAW_INSTANCE = 2, DRW_CMD_DRAW_INSTANCE_RANGE = 3, DRW_CMD_DRAW_PROCEDURAL = 4, - - /* Compute Commands. */ - DRW_CMD_COMPUTE = 8, - /* Other Commands */ DRW_CMD_CLEAR = 12, DRW_CMD_DRWSTATE = 13, @@ -228,12 +224,6 @@ typedef struct DRWCommandDrawInstanceRange { uint inst_count; } DRWCommandDrawInstanceRange; -typedef struct DRWCommandCompute { - int groups_x_len; - int groups_y_len; - int groups_z_len; -} DRWCommandCompute; - typedef struct DRWCommandDrawProcedural { GPUBatch *batch; DRWResourceHandle handle; @@ -270,7 +260,6 @@ typedef union DRWCommand { DRWCommandDrawInstance instance; DRWCommandDrawInstanceRange instance_range; DRWCommandDrawProcedural procedural; - DRWCommandCompute compute; DRWCommandSetMutableState state; DRWCommandSetStencil stencil; DRWCommandSetSelectID select_id; @@ -285,7 +274,6 @@ struct DRWCallBuffer { }; /** Used by #DRWUniform.type */ -/* TODO(jbakker): rename to DRW_RESOURCE/DRWResourceType. */ typedef enum { DRW_UNIFORM_INT = 0, DRW_UNIFORM_INT_COPY, @@ -298,7 +286,6 @@ typedef enum { DRW_UNIFORM_BLOCK, DRW_UNIFORM_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, - DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, /** Per drawcall uniforms/UBO */ DRW_UNIFORM_BLOCK_OBMATS, DRW_UNIFORM_BLOCK_OBINFOS, diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 3b852e7f8c8..6bdc5305fed 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -47,7 +47,6 @@ #endif #include "GPU_buffers.h" -#include "GPU_capabilities.h" #include "GPU_material.h" #include "GPU_uniform_buffer.h" @@ -447,19 +446,6 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, } } -void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, - const char *name, - GPUVertBuf *vertex_buffer) -{ - int location = GPU_shader_get_ssbo(shgroup->shader, name); - if (location == -1) { - BLI_assert(false && "Unable to locate binding of shader storage buffer objects."); - return; - } - drw_shgroup_uniform_create_ex( - shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -714,17 +700,6 @@ static void drw_command_draw_intance_range( cmd->inst_count = count; } -static void drw_command_compute(DRWShadingGroup *shgroup, - int groups_x_len, - int groups_y_len, - int groups_z_len) -{ - DRWCommandCompute *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE); - cmd->groups_x_len = groups_x_len; - cmd->groups_y_len = groups_y_len; - cmd->groups_z_len = groups_z_len; -} - static void drw_command_draw_procedural(DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, @@ -840,17 +815,6 @@ void DRW_shgroup_call_instance_range( drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); } -void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, - int groups_x_len, - int groups_y_len, - int groups_z_len) -{ - BLI_assert(groups_x_len > 0 && groups_y_len > 0 && groups_z_len > 0); - BLI_assert(GPU_compute_shader_support()); - - drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len); -} - static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup, GPUBatch *geom, Object *ob, diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index f29caebeb84..4c8fcb0e016 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -29,7 +29,6 @@ #include "BKE_global.h" -#include "GPU_compute.h" #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_state.h" @@ -673,9 +672,6 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, ((GPUVertBuf *)uni->pvalue)); break; - case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE: - GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location); - break; /* Legacy/Fallback support. */ case DRW_UNIFORM_BASE_INSTANCE: state->baseinst_loc = uni->location; @@ -1054,12 +1050,6 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) cmd->instance_range.inst_count, false); break; - case DRW_CMD_COMPUTE: - GPU_compute_dispatch(shgroup->shader, - cmd->compute.groups_x_len, - cmd->compute.groups_y_len, - cmd->compute.groups_z_len); - break; } } diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index 02c335ddae2..8684d82f228 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -28,9 +28,6 @@ uniform bool hairCloseTip = true; uniform vec4 hairDupliMatrix[4]; -/* Strand batch offset when used in compute shaders. */ -uniform int hairStrandOffset = 0; - /* -- Per control points -- */ uniform samplerBuffer hairPointBuffer; /* RGBA32F */ #define point_position xyz @@ -46,37 +43,13 @@ uniform usamplerBuffer hairStrandSegBuffer; /* R16UI */ /* -- Subdivision stage -- */ /** - * We use a transform feedback or compute shader to preprocess the strands and add more subdivision - * to it. For the moment these are simple smooth interpolation but one could hope to see the full + * We use a transform feedback to preprocess the strands and add more subdivision to it. + * For the moment these are simple smooth interpolation but one could hope to see the full * children particle modifiers being evaluated at this stage. * * If no more subdivision is needed, we can skip this step. */ -#ifdef GPU_VERTEX_SHADER -float hair_get_local_time() -{ - return float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1); -} - -int hair_get_id() -{ - return gl_VertexID / hairStrandsRes; -} -#endif - -#ifdef GPU_COMPUTE_SHADER -float hair_get_local_time() -{ - return float(gl_GlobalInvocationID.y) / float(hairStrandsRes - 1); -} - -int hair_get_id() -{ - return int(gl_GlobalInvocationID.x) + hairStrandOffset; -} -#endif - #ifdef HAIR_PHASE_SUBDIV int hair_get_base_id(float local_time, int strand_segments, out float interp_time) { @@ -91,9 +64,9 @@ int hair_get_base_id(float local_time, int strand_segments, out float interp_tim void hair_get_interp_attrs( out vec4 data0, out vec4 data1, out vec4 data2, out vec4 data3, out float interp_time) { - float local_time = hair_get_local_time(); + float local_time = float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1); - int hair_id = hair_get_id(); + int hair_id = gl_VertexID / hairStrandsRes; int strand_offset = int(texelFetch(hairStrandBuffer, hair_id).x); int strand_segments = int(texelFetch(hairStrandSegBuffer, hair_id).x); @@ -123,7 +96,6 @@ void hair_get_interp_attrs( */ #if !defined(HAIR_PHASE_SUBDIV) && defined(GPU_VERTEX_SHADER) - int hair_get_strand_id(void) { return gl_VertexID / (hairStrandsRes * hairThicknessRes); @@ -255,45 +227,3 @@ vec2 hair_resolve_barycentric(vec2 vert_barycentric) return vec2(1.0 - vert_barycentric.x, 0.0); } } - -/* Hair interpolation functions. */ -vec4 hair_get_weights_cardinal(float t) -{ - float t2 = t * t; - float t3 = t2 * t; -#if defined(CARDINAL) - float fc = 0.71; -#else /* defined(CATMULL_ROM) */ - float fc = 0.5; -#endif - - vec4 weights; - /* GLSL Optimized version of key_curve_position_weights() */ - float fct = t * fc; - float fct2 = t2 * fc; - float fct3 = t3 * fc; - weights.x = (fct2 * 2.0 - fct3) - fct; - weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0; - weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct; - weights.w = fct3 - fct2; - return weights; -} - -/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */ -vec4 hair_get_weights_bspline(float t) -{ - float t2 = t * t; - float t3 = t2 * t; - - vec4 weights; - /* GLSL Optimized version of key_curve_position_weights() */ - weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666); - weights.y = (0.5 * t3 - t2 + 0.66666666); - weights.w = (0.16666666 * t3); - return weights; -} - -vec4 hair_interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w) -{ - return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w; -} diff --git a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl deleted file mode 100644 index 4dcde4b0245..00000000000 --- a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl +++ /dev/null @@ -1,24 +0,0 @@ - -/* - * To be compiled with common_hair_lib.glsl. - */ - -layout(local_size_x = 1, local_size_y = 1) in; -layout(std430, binding = 0) writeonly buffer hairPointOutputBuffer -{ - vec4 posTime[]; -} -out_vertbuf; - -void main(void) -{ - float interp_time; - vec4 data0, data1, data2, data3; - hair_get_interp_attrs(data0, data1, data2, data3, interp_time); - - vec4 weights = hair_get_weights_cardinal(interp_time); - vec4 result = hair_interp_data(data0, data1, data2, data3, weights); - - uint index = uint(hair_get_id() * hairStrandsRes) + gl_GlobalInvocationID.y; - out_vertbuf.posTime[index] = result; -} diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl index 371d43827b9..3f5e3f8226f 100644 --- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl +++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl @@ -3,6 +3,47 @@ out vec4 finalColor; +vec4 get_weights_cardinal(float t) +{ + float t2 = t * t; + float t3 = t2 * t; +#if defined(CARDINAL) + float fc = 0.71; +#else /* defined(CATMULL_ROM) */ + float fc = 0.5; +#endif + + vec4 weights; + /* GLSL Optimized version of key_curve_position_weights() */ + float fct = t * fc; + float fct2 = t2 * fc; + float fct3 = t3 * fc; + weights.x = (fct2 * 2.0 - fct3) - fct; + weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0; + weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct; + weights.w = fct3 - fct2; + return weights; +} + +/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */ +vec4 get_weights_bspline(float t) +{ + float t2 = t * t; + float t3 = t2 * t; + + vec4 weights; + /* GLSL Optimized version of key_curve_position_weights() */ + weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666); + weights.y = (0.5 * t3 - t2 + 0.66666666); + weights.w = (0.16666666 * t3); + return weights; +} + +vec4 interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w) +{ + return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w; +} + #ifdef TF_WORKAROUND uniform int targetWidth; uniform int targetHeight; @@ -15,8 +56,8 @@ void main(void) vec4 data0, data1, data2, data3; hair_get_interp_attrs(data0, data1, data2, data3, interp_time); - vec4 weights = hair_get_weights_cardinal(interp_time); - finalColor = hair_interp_data(data0, data1, data2, data3, weights); + vec4 weights = get_weights_cardinal(interp_time); + finalColor = interp_data(data0, data1, data2, data3, weights); #ifdef TF_WORKAROUND int id = gl_VertexID - idOffset; diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index 0c054d4f264..45c656b49be 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -37,8 +37,6 @@ int GPU_max_textures(void); int GPU_max_textures_vert(void); int GPU_max_textures_geom(void); int GPU_max_textures_frag(void); -int GPU_max_work_group_count(int index); -int GPU_max_work_group_size(int index); int GPU_max_uniforms_vert(void); int GPU_max_uniforms_frag(void); int GPU_max_batch_indices(void); diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index c6e9dc210cb..bedc9ad3092 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -82,16 +82,6 @@ int GPU_max_textures(void) return GCaps.max_textures; } -int GPU_max_work_group_count(int index) -{ - return GCaps.max_work_group_count[index]; -} - -int GPU_max_work_group_size(int index) -{ - return GCaps.max_work_group_size[index]; -} - int GPU_max_uniforms_vert(void) { return GCaps.max_uniforms_vert; diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 95cf7fd335d..ee7ef1e69e6 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -41,8 +41,6 @@ struct GPUCapabilities { int max_textures_vert = 0; int max_textures_geom = 0; int max_textures_frag = 0; - int max_work_group_count[3] = {0, 0, 0}; - int max_work_group_size[3] = {0, 0, 0}; int max_uniforms_vert = 0; int max_uniforms_frag = 0; int max_batch_indices = 0; diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index d85f9f7684d..fb03a2c2d2a 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -438,14 +438,6 @@ 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; - 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]); - glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &GCaps.max_work_group_count[2]); - glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &GCaps.max_work_group_size[0]); - glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &GCaps.max_work_group_size[1]); - glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]); - } GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object; /* GL specific capabilities. */ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size); -- cgit v1.2.3 From e5b51cb511e8ea408ec14017f9b13821d4299726 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 26 May 2021 21:12:38 -0400 Subject: Fix T88603: Crash with spline attributes after curve resample The output curve's spline attribute domain custom data needs to be reallocated with the correct length after adding the splines. --- source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc | 2 ++ 1 file changed, 2 insertions(+) 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 1c42b9341a0..684f7d6c702 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -182,6 +182,8 @@ static std::unique_ptr resample_curve(const CurveEval &input_curve, } } + output_curve->attributes.reallocate(output_curve->splines().size()); + return output_curve; } -- cgit v1.2.3 From 6fc9ec92574ef55f76eca367565b8b184466b0e8 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 26 May 2021 21:28:05 -0300 Subject: Cleanup: Specify amount of buffers through preprocessor directives --- source/blender/draw/intern/draw_cache_extract.h | 4 ++++ source/blender/draw/intern/draw_cache_impl_mesh.c | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index bbb97fc09a3..a3e43ff2ec5 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -264,6 +264,10 @@ typedef struct MeshBatchCache { bool no_loose_wire; } MeshBatchCache; +#define MBC_BATCH_LEN (sizeof(((MeshBatchCache){0}).batch) / sizeof(void *)) +#define MBC_VBO_LEN (sizeof(((MeshBufferCache){0}).vbo) / sizeof(void *)) +#define MBC_IBO_LEN (sizeof(((MeshBufferCache){0}).ibo) / sizeof(void *)) + void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache mbc, diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index af54b57b162..8d5fdcc5276 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -1160,25 +1160,25 @@ static void drw_mesh_batch_cache_check_available(struct TaskGraph *task_graph, M * some issues (See T77867 where we needed to disable this function in order to debug what was * happening in release builds). */ BLI_task_graph_work_and_wait(task_graph); - for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) { + for (int i = 0; i < MBC_BATCH_LEN; i++) { BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0)); } - for (int i = 0; i < sizeof(cache->final.vbo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_VBO_LEN; i++) { BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.vbo)[i])); } - for (int i = 0; i < sizeof(cache->final.ibo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_IBO_LEN; i++) { BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.ibo)[i])); } - for (int i = 0; i < sizeof(cache->cage.vbo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_VBO_LEN; i++) { BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.vbo)[i])); } - for (int i = 0; i < sizeof(cache->cage.ibo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_IBO_LEN; i++) { BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.ibo)[i])); } - for (int i = 0; i < sizeof(cache->uv_cage.vbo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_VBO_LEN; i++) { BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.vbo)[i])); } - for (int i = 0; i < sizeof(cache->uv_cage.ibo) / sizeof(void *); i++) { + for (int i = 0; i < MBC_IBO_LEN; i++) { BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.ibo)[i])); } } -- cgit v1.2.3 From f3944cf503966a93a124e389d9232d7f833c0077 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Wed, 26 May 2021 20:02:35 -0600 Subject: Win: Add launcher to hide the console window flash This patch fixes a long-standing complaint from users: the console window shortly flashing when they start blender. This is done by adding a new executable called blender-launcher.exe which starts blender.exe while hiding the console. Any command line parameters given to blender-launcher will be passed on to blender.exe so it'll be a drop in replacement. Starting blender.exe on its own will still function as a proper console app so no changes required here for users that use blender for batch processing. Notable changes: Registering blender (-R switch) will now register blender-launcher as the preferred executable. This patch updates the installer and updates the shortcuts to start blender-launcher.exe rather than blender.exe Differential Revision: https://developer.blender.org/D11094 Reviewed by: brecht, harley --- build_files/cmake/packaging.cmake | 4 +- source/blender/blenlib/intern/winstuff.c | 4 +- source/blender/windowmanager/intern/wm_init_exit.c | 9 ++- source/creator/CMakeLists.txt | 11 ++- source/creator/blender-launcher.c | 92 ++++++++++++++++++++++ 5 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 source/creator/blender-launcher.c diff --git a/build_files/cmake/packaging.cmake b/build_files/cmake/packaging.cmake index 4a0a4f2493d..265b3c0e2ab 100644 --- a/build_files/cmake/packaging.cmake +++ b/build_files/cmake/packaging.cmake @@ -104,8 +104,8 @@ if(WIN32) set(CPACK_WIX_LIGHT_EXTRA_FLAGS -dcl:medium) endif() -set(CPACK_PACKAGE_EXECUTABLES "blender" "blender") -set(CPACK_CREATE_DESKTOP_LINKS "blender" "blender") +set(CPACK_PACKAGE_EXECUTABLES "blender-launcher" "blender") +set(CPACK_CREATE_DESKTOP_LINKS "blender-launcher" "blender") include(CPack) diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index 333b6783087..3aa61d1fec5 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -94,9 +94,9 @@ void BLI_windows_register_blend_extension(const bool background) GetModuleFileName(0, BlPath, MAX_PATH); /* Replace the actual app name with the wrapper. */ - blender_app = strstr(BlPath, "blender-app.exe"); + blender_app = strstr(BlPath, "blender.exe"); if (blender_app != NULL) { - strcpy(blender_app, "blender.exe"); + strcpy(blender_app, "blender-launcher.exe"); } /* root is HKLM by default */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 56fd51ac6fd..0dcb817ad15 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -343,8 +343,13 @@ void WM_init(bContext *C, int argc, const char **argv) (void)argv; /* unused */ #endif - if (!G.background && !wm_start_with_console) { - GHOST_toggleConsole(3); + if (!G.background) { + if (wm_start_with_console) { + GHOST_toggleConsole(1); + } + else { + GHOST_toggleConsole(3); + } } BKE_material_copybuf_clear(); diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index c2893317924..baea3ad1f9b 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -285,6 +285,15 @@ if(WITH_PYTHON_MODULE) else() add_executable(blender ${EXETYPE} ${SRC}) + if(WIN32) + add_executable(blender-launcher WIN32 + blender-launcher.c + ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.rc + ${CMAKE_BINARY_DIR}/blender.exe.manifest + ) + target_compile_definitions (blender-launcher PRIVATE -D_UNICODE -DUNICODE) + target_link_libraries(blender-launcher Pathcch.lib) + endif() endif() if(WITH_BUILDINFO) @@ -1212,7 +1221,7 @@ endif() if(WIN32 AND NOT WITH_PYTHON_MODULE) install( - TARGETS blender + TARGETS blender blender-launcher COMPONENT Blender DESTINATION "." ) diff --git a/source/creator/blender-launcher.c b/source/creator/blender-launcher.c new file mode 100644 index 00000000000..86b0f4f3b97 --- /dev/null +++ b/source/creator/blender-launcher.c @@ -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 +#include + +#include + +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) +{ + STARTUPINFO siStartInfo = {0}; + PROCESS_INFORMATION procInfo; + wchar_t path[MAX_PATH]; + + siStartInfo.wShowWindow = SW_HIDE; + siStartInfo.dwFlags = STARTF_USESHOWWINDOW; + + /* Get the path to the currently running executable (blender-launcher.exe) */ + + DWORD nSize = GetModuleFileName(NULL, path, MAX_PATH); + if (!nSize) { + return -1; + } + + /* GetModuleFileName returns the number of characters written, but GetLastError needs to be + * called to see if it ran out of space or not. However where would we be without exceptions + * to the rule: "If the buffer is too small to hold the module name, the function returns nSize. + * The last error code remains ERROR_SUCCESS." - source: MSDN. */ + + if (GetLastError() == ERROR_SUCCESS && nSize == MAX_PATH) { + return -1; + } + + /* Remove the filename (blender-launcher.exe) from path. */ + if (PathCchRemoveFileSpec(path, MAX_PATH) != S_OK) { + return -1; + } + + /* Add blender.exe to path, resulting in the full path to the blender executable. */ + if (PathCchCombine(path, MAX_PATH, path, L"blender.exe") != S_OK) { + return -1; + } + + int required_size_chars = lstrlenW(path) + /* Module name */ + 3 + /* 2 quotes + Space */ + lstrlenW(pCmdLine) + /* Original command line */ + 1; /* Zero terminator */ + size_t required_size_bytes = required_size_chars * sizeof(wchar_t); + wchar_t *buffer = (wchar_t *)malloc(required_size_bytes); + if (!buffer) { + return -1; + } + + if (StringCbPrintfEx(buffer, + required_size_bytes, + NULL, + NULL, + STRSAFE_NULL_ON_FAILURE, + L"\"%s\" %s", + path, + pCmdLine) != S_OK) { + free(buffer); + return -1; + } + + BOOL success = CreateProcess( + path, buffer, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &procInfo); + + if (success) { + /* Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer + * needed - MSDN. Closing the handles will NOT terminate the thread/process that we just + * started. */ + CloseHandle(procInfo.hThread); + CloseHandle(procInfo.hProcess); + } + + free(buffer); + return success ? 0 : -1; +} -- cgit v1.2.3 From c97b6215a37e5eca744d54d2e80741af78e1dafc Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 26 May 2021 22:14:59 -0400 Subject: Geometry Nodes: Support interpolation between curve domains This commit adds interpolation from the point domain to the spline domain and the other way around. Before this, spline domain attributes were basically useless, but now they are quite helpful as a way to use a shared value in a contiguous group of points. I implementented a special virtual array for the spline to points conversion, so that conversion should be close to the ideal performance level, but there are a few ways we could optimize the point to spline conversion in the future: - Use a function virtual array to mix the point values for each spline on demand. - Implement a special case for when the input virtual array is one of the virtual arrays from the spline point attributes. In other words, decrease curve attribute access overhead. Differential Revision: https://developer.blender.org/D11376 --- source/blender/blenkernel/BKE_geometry_set.hh | 4 + .../blenkernel/intern/geometry_component_curve.cc | 183 +++++++++++++++++++-- 2 files changed, 171 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 3b3856f11ab..0ed6eea7954 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -420,6 +420,10 @@ class CurveComponent : public GeometryComponent { CurveEval *get_for_write(); int attribute_domain_size(const AttributeDomain domain) const final; + std::unique_ptr attribute_try_adapt_domain( + std::unique_ptr varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const final; bool is_empty() const final; diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index d08681da6ec..b9930e2fc50 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -14,11 +14,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BKE_spline.hh" - #include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_geometry_set.hh" +#include "BKE_spline.hh" #include "attribute_access_intern.hh" @@ -26,6 +25,7 @@ using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray_For_GSpan; using blender::fn::GVArray_GSpan; +using blender::fn::GVArrayPtr; using blender::fn::GVMutableArray_For_GMutableSpan; /* -------------------------------------------------------------------- */ @@ -142,6 +142,171 @@ int CurveComponent::attribute_domain_size(const AttributeDomain domain) const return 0; } +namespace blender::bke { + +namespace { +struct PointIndices { + int spline_index; + int point_index; +}; +} // namespace +static PointIndices lookup_point_indices(Span offsets, const int index) +{ + const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) - + offsets.begin() - 1; + const int index_in_spline = index - offsets[spline_index]; + return {spline_index, index_in_spline}; +} + +/** + * Mix together all of a spline's control point values. + * + * \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_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); + attribute_math::DefaultMixer mixer(r_values); + + 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)) { + const T value = old_values[spline_offset + i_point]; + mixer.mix_in(i_spline, value); + } + } + + mixer.finalize(); +} + +static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray) +{ + GVArrayPtr new_varray; + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v>) { + Array values(curve.splines().size()); + adapt_curve_domain_point_to_spline_impl(curve, varray->typed(), values); + new_varray = std::make_unique>>(std::move(values)); + } + }); + return new_varray; +} + +/** + * A virtual array implementation for the conversion of spline attributes to control point + * attributes. The goal is to avoid copying the spline value for every one of its control points + * unless it is necessary (in that case the materialize functions will be called). + */ +template class VArray_For_SplineToPoint final : public VArray { + /* Store existing data materialized if it was not already a span. This is expected + * to be worth it because a single spline's value will likely be accessed many times. */ + VArray_Span original_data_; + Array offsets_; + + public: + VArray_For_SplineToPoint(const VArray &original_varray, Array offsets) + : VArray(offsets.last()), original_data_(original_varray), offsets_(std::move(offsets)) + { + } + + T get_impl(const int64_t index) const final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + return original_data_[indices.spline_index]; + } + + void materialize_impl(const IndexMask mask, MutableSpan r_span) const final + { + const int total_size = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + for (const int spline_index : original_data_.index_range()) { + const int offset = offsets_[spline_index]; + const int next_offset = offsets_[spline_index + 1]; + r_span.slice(offset, next_offset - offset).fill(original_data_[spline_index]); + } + } + else { + int spline_index = 0; + for (const int dst_index : mask) { + while (offsets_[spline_index] < dst_index) { + spline_index++; + } + r_span[dst_index] = original_data_[spline_index]; + } + } + } + + void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan r_span) const final + { + T *dst = r_span.data(); + const int total_size = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + for (const int spline_index : original_data_.index_range()) { + const int offset = offsets_[spline_index]; + const int next_offset = offsets_[spline_index + 1]; + uninitialized_fill_n(dst + offset, next_offset - offset, original_data_[spline_index]); + } + } + else { + int spline_index = 0; + for (const int dst_index : mask) { + while (offsets_[spline_index] < dst_index) { + spline_index++; + } + new (dst + dst_index) T(original_data_[spline_index]); + } + } + } +}; + +static GVArrayPtr adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArrayPtr varray) +{ + GVArrayPtr new_varray; + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { + using T = decltype(dummy); + + Array offsets = curve.control_point_offsets(); + new_varray = std::make_unique>>( + offsets.last(), *varray->typed(), std::move(offsets)); + }); + return new_varray; +} + +} // namespace blender::bke + +GVArrayPtr CurveComponent::attribute_try_adapt_domain(GVArrayPtr varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const +{ + if (!varray) { + return {}; + } + if (varray->size() == 0) { + return {}; + } + if (from_domain == to_domain) { + return varray; + } + + if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) { + return blender::bke::adapt_curve_domain_point_to_spline(*curve_, std::move(varray)); + } + if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) { + return blender::bke::adapt_curve_domain_spline_to_point(*curve_, std::move(varray)); + } + + return {}; +} + static CurveEval *get_curve_from_component_for_write(GeometryComponent &component) { BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); @@ -300,20 +465,6 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve) * array implementations try to make it workable in common situations. * \{ */ -namespace { -struct PointIndices { - int spline_index; - int point_index; -}; -} // namespace -static PointIndices lookup_point_indices(Span offsets, const int index) -{ - const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) - - offsets.begin() - 1; - const int index_in_spline = index - offsets[spline_index]; - return {spline_index, index_in_spline}; -} - template static void point_attribute_materialize(Span> data, Span offsets, -- cgit v1.2.3 From b132dd042e7dd7f589644e1edaacdf5e07608398 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 26 May 2021 22:22:09 -0400 Subject: Cleanup: Simplify spline point attribute materialize functions - Iterate over the mask directly instead of using an index. - Use Span slice and copy_from instead of a lower level function. --- source/blender/blenkernel/intern/geometry_component_curve.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index b9930e2fc50..be3fdf26cb3 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -476,14 +476,12 @@ static void point_attribute_materialize(Span> data, for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; - initialized_copy_n(data[spline_index].data(), next_offset - offset, r_span.data() + offset); + r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]); } } else { int spline_index = 0; - for (const int i : r_span.index_range()) { - const int dst_index = mask[i]; - + for (const int dst_index : mask) { while (offsets[spline_index] < dst_index) { spline_index++; } @@ -511,9 +509,7 @@ static void point_attribute_materialize_to_uninitialized(Span> data, } else { int spline_index = 0; - for (const int i : r_span.index_range()) { - const int dst_index = mask[i]; - + for (const int dst_index : mask) { while (offsets[spline_index] < dst_index) { spline_index++; } -- cgit v1.2.3 From a10e41b02e66aed2d987bff9b8732a28ef0c4a26 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 26 May 2021 22:32:53 -0400 Subject: Cleanup: Remove completed "TODO" comment Since rBb67fe05d4bea, the dependency graph supports relations on collection geometry, and the nodes modifier uses that. --- source/blender/modifiers/intern/MOD_nodes.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index f927f4b1bcd..29c04d6c571 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -207,8 +207,6 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } } - - /* TODO: Add dependency for adding and removing objects in collections. */ } static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) -- cgit v1.2.3 From deb71cef38b6482d513d2e85dc540b40419897e8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 27 May 2021 16:14:51 +1000 Subject: Undo: resolve inefficient edit-mesh memory use with multiple objects When editing more than 1 object at a time, complete copies of each mesh were being stored. Now the most recent undo-data for each mesh is used (when available). --- source/blender/editors/mesh/editmesh_undo.c | 80 ++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 79385e28aa9..a981482cd82 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -520,11 +520,63 @@ static void um_arraystore_free(UndoMesh *um) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Array Store Utilities + * \{ */ + +/** + * Create an array of #UndoMesh from `objects`. + * + * where each element in the resulting array is the most recently created + * undo-mesh for the object's mesh. + * When no undo-mesh can be found that array index is NULL. + * + * This is used for de-duplicating memory between undo steps, + * failure to find the undo step will store a full duplicate in memory. + * define `DEBUG_PRINT` to check memory is de-duplicating as expected. + */ +static UndoMesh **mesh_undostep_reference_elems_from_objects(Object **object, int object_len) +{ + /* Map: `Mesh.id.session_uuid` -> `UndoMesh`. */ + GHash *uuid_map = BLI_ghash_ptr_new_ex(__func__, object_len); + UndoMesh **um_references = MEM_callocN(sizeof(UndoMesh *) * object_len, __func__); + for (int i = 0; i < object_len; i++) { + const Mesh *me = object[i]->data; + BLI_ghash_insert(uuid_map, POINTER_FROM_INT(me->id.session_uuid), &um_references[i]); + } + int uuid_map_len = object_len; + + /* Loop backwards over all previous mesh undo data until either: + * - All elements have been found (where `um_references` we'll have every element set). + * - There are no undo steps left to look for. */ + LinkData *link = um_arraystore.local_links.last; + while (link && uuid_map_len != 0) { + UndoMesh *um_iter = link->data, **um_p; + if ((um_p = BLI_ghash_popkey(uuid_map, POINTER_FROM_INT(um_iter->me.id.session_uuid), NULL))) { + *um_p = um_iter; + uuid_map_len--; + } + link = link->prev; + } + BLI_assert(uuid_map_len == BLI_ghash_len(uuid_map)); + BLI_ghash_free(uuid_map, NULL, NULL); + if (uuid_map_len == object_len) { + MEM_freeN(um_references); + um_references = NULL; + } + return um_references; +} + +/** \} */ + #endif /* USE_ARRAY_STORE */ /* for callbacks */ /* undo simply makes copies of a bmesh */ -static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) +/** + * \param um_ref: The reference to use for de-duplicating memory between undo-steps. + */ +static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, UndoMesh *um_ref) { BLI_assert(BLI_array_is_zeroed(um, 1)); #ifdef USE_ARRAY_STORE_THREAD @@ -560,12 +612,6 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) #ifdef USE_ARRAY_STORE { - /* We could be more clever here, - * the previous undo state may be from a separate mesh. */ - const UndoMesh *um_ref = um_arraystore.local_links.last ? - ((LinkData *)um_arraystore.local_links.last)->data : - NULL; - /* Add ourselves. */ BLI_addtail(&um_arraystore.local_links, BLI_genericNodeN(um)); @@ -583,6 +629,8 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) um_arraystore_compact_with_info(um, um_ref); # endif } +#else + UNUSED_VARS(um_ref); #endif return um; @@ -749,6 +797,12 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); us->elems_len = objects_len; + UndoMesh **um_references = NULL; + +#ifdef USE_ARRAY_STORE + um_references = mesh_undostep_reference_elems_from_objects(objects, objects_len); +#endif + for (uint i = 0; i < objects_len; i++) { Object *ob = objects[i]; MeshUndoStep_Elem *elem = &us->elems[i]; @@ -756,12 +810,22 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und elem->obedit_ref.ptr = ob; Mesh *me = elem->obedit_ref.ptr->data; BMEditMesh *em = me->edit_mesh; - undomesh_from_editmesh(&elem->data, me->edit_mesh, me->key); + undomesh_from_editmesh( + &elem->data, me->edit_mesh, me->key, um_references ? um_references[i] : NULL); em->needs_flush_to_id = 1; us->step.data_size += elem->data.undo_size; + +#ifdef USE_ARRAY_STORE + /** As this is only data storage it is safe to set the session ID here. */ + elem->data.me.id.session_uuid = me->id.session_uuid; +#endif } MEM_freeN(objects); + if (um_references != NULL) { + MEM_freeN(um_references); + } + bmain->is_memfile_undo_flush_needed = true; return true; -- cgit v1.2.3 From 7d20cf92dd2f975d651bcfac686863e00d631b08 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 27 May 2021 16:34:41 +1000 Subject: Cleanup: use UndoMesh as links instead of allocating LinkData While the advantage isn't large, it's simpler to skip the intermediate link. Also remove unused next and previous struct members from MeshUndoStep_Elem. --- source/blender/editors/mesh/editmesh_undo.c | 30 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index a981482cd82..112de68b52c 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -93,6 +93,12 @@ typedef struct BArrayCustomData { #endif typedef struct UndoMesh { + /** + * This undo-meshes in `um_arraystore.local_links`. + * Not to be confused with the next and previous undo steps. + */ + struct UndoMesh *local_next, *local_prev; + Mesh me; int selectmode; @@ -128,7 +134,10 @@ static struct { struct BArrayStore_AtSize bs_stride; int users; - /* We could have the undo API pass in the previous state, for now store a local list */ + /** + * A list of #UndoMesh items ordered from oldest to newest + * used to access previous undo data for a mesh. + */ ListBase local_links; # ifdef USE_ARRAY_STORE_THREAD @@ -549,14 +558,14 @@ static UndoMesh **mesh_undostep_reference_elems_from_objects(Object **object, in /* Loop backwards over all previous mesh undo data until either: * - All elements have been found (where `um_references` we'll have every element set). * - There are no undo steps left to look for. */ - LinkData *link = um_arraystore.local_links.last; - while (link && uuid_map_len != 0) { - UndoMesh *um_iter = link->data, **um_p; + UndoMesh *um_iter = um_arraystore.local_links.last; + while (um_iter && (uuid_map_len != 0)) { + UndoMesh **um_p; if ((um_p = BLI_ghash_popkey(uuid_map, POINTER_FROM_INT(um_iter->me.id.session_uuid), NULL))) { *um_p = um_iter; uuid_map_len--; } - link = link->prev; + um_iter = um_iter->local_prev; } BLI_assert(uuid_map_len == BLI_ghash_len(uuid_map)); BLI_ghash_free(uuid_map, NULL, NULL); @@ -613,7 +622,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo #ifdef USE_ARRAY_STORE { /* Add ourselves. */ - BLI_addtail(&um_arraystore.local_links, BLI_genericNodeN(um)); + BLI_addtail(&um_arraystore.local_links, um); # ifdef USE_ARRAY_STORE_THREAD if (um_arraystore.task_pool == NULL) { @@ -730,11 +739,9 @@ static void undomesh_free_data(UndoMesh *um) /* we need to expand so any allocations in custom-data are freed with the mesh */ um_arraystore_expand(um); - { - LinkData *link = BLI_findptr(&um_arraystore.local_links, um, offsetof(LinkData, data)); - BLI_remlink(&um_arraystore.local_links, link); - MEM_freeN(link); - } + BLI_assert(BLI_findindex(&um_arraystore.local_links, um) != -1); + BLI_remlink(&um_arraystore.local_links, um); + um_arraystore_free(um); #endif @@ -768,7 +775,6 @@ static Object *editmesh_object_from_context(bContext *C) * \{ */ typedef struct MeshUndoStep_Elem { - struct MeshUndoStep_Elem *next, *prev; UndoRefID_Object obedit_ref; UndoMesh data; } MeshUndoStep_Elem; -- cgit v1.2.3 From 223c6e1ead2940a89465ff66765d16ac14a992b7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 27 May 2021 09:43:11 +0200 Subject: Geometry Nodes: disable multi-threading in evaluator for now A deadlock could happen under certain circumstances when geometry nodes is used on multiple objects. Once T88598 is resolved, multi-threading can be enabled again. Differential Revision: https://developer.blender.org/D11405 --- source/blender/modifiers/intern/MOD_nodes_evaluator.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index a5c6d0abce0..10ef2f4d8eb 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -380,7 +380,8 @@ class GeometryNodesEvaluator { void execute() { - task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH); + /* Disable threading until T88598 is resolved. */ + task_pool_ = BLI_task_pool_create_no_threads(this); this->create_states_for_reachable_nodes(); this->forward_group_inputs(); -- cgit v1.2.3 From 2ad3a1c3189ae7f5b50777d4fa2040aae857495c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 27 May 2021 09:58:45 +0200 Subject: Nodes: fix material node copied over when socket is copied This was missing from rB207472930834a2916cf18bbdff51bcd77c6dd0c0. --- source/blender/nodes/intern/node_socket.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 783a7a9b3d8..052896d2f48 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -37,6 +37,7 @@ #include "BKE_node.h" #include "DNA_collection_types.h" +#include "DNA_material_types.h" #include "RNA_access.h" #include "RNA_types.h" @@ -396,6 +397,13 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from) id_us_plus(&toval->value->id); break; } + case SOCK_MATERIAL: { + bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to->default_value; + bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from->default_value; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } } to->flag |= (from->flag & SOCK_HIDE_VALUE); -- cgit v1.2.3 From f0342065b18f4dd9570c57da4b10c7dbe24fbaa7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 27 May 2021 17:02:44 +1000 Subject: Cleanup: spelling --- source/blender/blenkernel/intern/gpencil.c | 2 +- source/blender/blenkernel/intern/lib_override.c | 8 ++++---- source/blender/blenlib/BLI_color.hh | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 409b9fb0a2c..6d1476485ca 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -2688,7 +2688,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, * This is used only in final render and never in Viewport. */ if ((view_layer != NULL) && (gpl->viewlayername[0] != '\0') && (!STREQ(view_layer->name, gpl->viewlayername))) { - /* Do not skip masks when rendering the viewlayer so that it can still be used to clip + /* Do not skip masks when rendering the view-layer so that it can still be used to clip * other layers. Instead set their opacity to zero. */ if (gpencil_is_layer_mask(view_layer, gpd, gpl)) { gpl->opacity = 0.0f; diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 36730a7baf7..1aa94f82eb3 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -357,9 +357,9 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, /* If `newid` is already set, assume it has been handled by calling code. * Only current use case: re-using proxy ID when converting to liboverride. */ if (reference_id->newid == NULL) { - /* Note: `no main` case is used during resync procedure, to support recursive resync. This - * requires extra care further odwn the resync process, see - * `BKE_lib_override_library_resync`. */ + /* Note: `no main` case is used during resync procedure, to support recursive resync. + * This requires extra care further down the resync process, + * see: #BKE_lib_override_library_resync. */ reference_id->newid = lib_override_library_create_from( bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0); if (reference_id->newid == NULL) { @@ -408,7 +408,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, * loop, but we can get to them through their reference's `newid` pointer. */ if (do_no_main && id->lib == reference_id->lib && id->newid != NULL) { other_id = id->newid; - /* Otherwise we cannot properly dinstinguish between IDs that are actually from the + /* Otherwise we cannot properly distinguish between IDs that are actually from the * linked library (and should not be remapped), and IDs that are overrides re-generated * from the reference from the linked library, and must therefore be remapped. * diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index 287587e04be..3b01bbfb86e 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -157,7 +157,7 @@ template class ColorSceneLinear4f; template class ColorSceneLinearByteEncoded4b; template class ColorTheme4; -/* Forward declation of precision conversion methods. */ +/* Forward declaration of precision conversion methods. */ BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4f(const ColorTheme4 &srgb4b); BLI_INLINE ColorTheme4 BLI_color_convert_to_theme4b(const ColorTheme4 &srgb4f); -- cgit v1.2.3 From 1276d0024fb66825ec219ba742187cfcf338cbd2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 27 May 2021 17:12:38 +1000 Subject: Cleanup: specify array sizes, remove warnings in comments --- source/blender/editors/space_file/file_ops.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 61f3c046550..36f8476d0c9 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -2263,23 +2263,24 @@ void FILE_OT_filepath_drop(wmOperatorType *ot) * \{ */ /** - * Create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created. + * Create a new, non-existing folder name, returns true if successful, + * false if name couldn't be created. * The actual name is returned in 'name', 'folder' contains the complete path, * including the new folder name. */ -static int new_folder_path(const char *parent, char *folder, char *name) +static bool new_folder_path(const char *parent, char folder[FILE_MAX], char name[FILE_MAXFILE]) { int i = 1; int len = 0; BLI_strncpy(name, "New Folder", FILE_MAXFILE); - BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */ + BLI_join_dirfile(folder, FILE_MAX, parent, name); /* check whether folder with the name already exists, in this case * add number to the name. Check length of generated name to avoid * crazy case of huge number of folders each named 'New Folder (x)' */ while (BLI_exists(folder) && (len < FILE_MAXFILE)) { len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i); - BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */ + BLI_join_dirfile(folder, FILE_MAX, parent, name); i++; } -- cgit v1.2.3 From 41f2ea4045b183b7e2d0c5f61d7ab7958267122e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 27 May 2021 17:16:08 +1000 Subject: Fix incorrect BLI_snprintf usage Event though in practice this wasn't causing problems as the fixed size buffers are generally large enough not to truncate text. Using the result from `snprint` or `BLI_snprintf` to step over a fixed size buffer allows for buffer overruns as the returned value is the size needed to copy the entire string, not the number of bytes copied. Building strings using this convention with multiple calls: ofs += BLI_snprintf(str + ofs, str_len_max - ofs); .. caused the size argument to become negative, wrapping it to a large value when cast to the unsigned argument. --- source/blender/blenkernel/intern/unit.c | 5 +- source/blender/blenlib/intern/timecode.c | 4 +- .../draw/engines/overlay/overlay_motion_path.c | 4 +- .../blender/editors/interface/interface_handlers.c | 2 +- .../editors/interface/interface_templates.c | 2 +- source/blender/editors/space_clip/clip_buttons.c | 6 +- source/blender/editors/space_image/image_buttons.c | 5 +- source/blender/editors/space_info/info_stats.c | 122 ++++++++++----------- source/blender/editors/transform/transform_mode.c | 82 +++++++------- .../transform/transform_mode_edge_seq_slide.c | 10 +- .../editors/transform/transform_mode_edge_slide.c | 8 +- .../transform/transform_mode_shrink_fatten.c | 6 +- .../transform/transform_mode_timetranslate.c | 4 +- .../editors/transform/transform_mode_trackball.c | 26 ++--- .../editors/transform/transform_mode_translate.c | 104 +++++++++--------- .../editors/transform/transform_mode_vert_slide.c | 8 +- source/blender/imbuf/intern/jpeg.c | 3 +- source/blender/python/mathutils/mathutils_Matrix.c | 2 +- 18 files changed, 204 insertions(+), 199 deletions(-) diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index 9ae1c754846..3612a26315c 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -943,7 +943,7 @@ static int unit_scale_str(char *str, /* Add the addition sign, the bias, and the close parenthesis after the value. */ int value_end_ofs = find_end_of_value_chars(str, len_max, prev_op_ofs + 2); - int len_bias_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias); + int len_bias_num = BLI_snprintf_rlen(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias); if (value_end_ofs + len_bias_num < len_max) { memmove(str + value_end_ofs + len_bias_num, str + value_end_ofs, len - value_end_ofs + 1); memcpy(str + value_end_ofs, str_tmp, len_bias_num); @@ -957,7 +957,8 @@ static int unit_scale_str(char *str, int len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator. */ /* "#" Removed later */ - int len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref); + int len_num = BLI_snprintf_rlen( + str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref); if (len_num > len_max) { len_num = len_max; diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c index 9586da941a4..7d7436411ac 100644 --- a/source/blender/blenlib/intern/timecode.c +++ b/source/blender/blenlib/intern/timecode.c @@ -216,10 +216,10 @@ size_t BLI_timecode_string_from_time_simple(char *str, const int hun = ((int)(fmod(time_seconds, 1.0) * 100)); if (hr) { - rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun); + rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun); } else { - rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun); + rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun); } return rlen; diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c index 48b7b53a5ba..a92f11aca38 100644 --- a/source/blender/draw/engines/overlay/overlay_motion_path.c +++ b/source/blender/draw/engines/overlay/overlay_motion_path.c @@ -190,7 +190,7 @@ static void motion_path_cache(OVERLAY_Data *vedata, bool is_keyframe = (mpv->flag & MOTIONPATH_VERT_KEY) != 0; if ((show_keyframes && show_keyframes_no && is_keyframe) || (show_frame_no && (i == 0))) { - numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), " %d", frame); DRW_text_cache_add( dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, (is_keyframe) ? col_kf : col); } @@ -200,7 +200,7 @@ static void motion_path_cache(OVERLAY_Data *vedata, /* Only draw frame number if several consecutive highlighted points * don't occur on same point. */ if ((equals_v3v3(mpv->co, mpvP->co) == 0) || (equals_v3v3(mpv->co, mpvN->co) == 0)) { - numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), " %d", frame); DRW_text_cache_add(dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, col); } } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 282d470c7ea..22a2e0a55d1 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2380,7 +2380,7 @@ static void float_array_to_string(float *values, current_index++; for (int i = 0; i < array_length; i++) { - int length = BLI_snprintf( + int length = BLI_snprintf_rlen( output + current_index, output_len_max - current_index, "%f", values[i]); current_index += length; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index e3df9704826..dad3ccbe213 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1078,7 +1078,7 @@ static void template_ID(const bContext *C, char numstr[32]; short numstr_len; - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id)); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id)); but = uiDefBut( block, diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index d555238e949..7379891543b 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -809,12 +809,12 @@ void uiTemplateMovieclipInformation(uiLayout *layout, char str[1024]; size_t ofs = 0; - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, TIP_("%d x %d"), width, height); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, TIP_("%d x %d"), width, height); if (ibuf) { if (ibuf->rect_float) { if (ibuf->channels != 4) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_(", %d float channel(s)"), ibuf->channels); } else if (ibuf->planes == R_IMF_PLANES_RGBA) { @@ -837,7 +837,7 @@ void uiTemplateMovieclipInformation(uiLayout *layout, short frs_sec; float frs_sec_base; if (IMB_anim_get_fps(clip->anim, &frs_sec, &frs_sec_base, true)) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_(", %.2f fps"), (float)frs_sec / frs_sec_base); } } diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 6fb64de7e85..d909bfd1864 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1218,11 +1218,12 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i const int len = MAX_IMAGE_INFO_LEN; int ofs = 0; - ofs += BLI_snprintf(str + ofs, len - ofs, TIP_("%d x %d, "), ibuf->x, ibuf->y); + ofs += BLI_snprintf_rlen(str + ofs, len - ofs, TIP_("%d x %d, "), ibuf->x, ibuf->y); if (ibuf->rect_float) { if (ibuf->channels != 4) { - ofs += BLI_snprintf(str + ofs, len - ofs, TIP_("%d float channel(s)"), ibuf->channels); + ofs += BLI_snprintf_rlen( + str + ofs, len - ofs, TIP_("%d float channel(s)"), ibuf->channels); } else if (ibuf->planes == R_IMF_PLANES_RGBA) { ofs += BLI_strncpy_rlen(str + ofs, TIP_(" RGBA float"), len - ofs); diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 0bdfceb36b6..cf847fa18a8 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -505,14 +505,14 @@ static void get_stats_string( LayerCollection *layer_collection = view_layer->active_collection; if (object_mode == OB_MODE_OBJECT) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - "%s | ", - BKE_collection_ui_name_get(layer_collection->collection)); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + "%s | ", + BKE_collection_ui_name_get(layer_collection->collection)); } if (ob) { - *ofs += BLI_snprintf(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2); + *ofs += BLI_snprintf_rlen(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2); } if (obedit) { @@ -521,72 +521,72 @@ static void get_stats_string( } if (obedit->type == OB_MESH) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"), - stats_fmt->totvertsel, - stats_fmt->totvert, - stats_fmt->totedgesel, - stats_fmt->totedge, - stats_fmt->totfacesel, - stats_fmt->totface, - stats_fmt->tottri); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"), + stats_fmt->totvertsel, + stats_fmt->totvert, + stats_fmt->totedgesel, + stats_fmt->totedge, + stats_fmt->totfacesel, + stats_fmt->totface, + stats_fmt->tottri); } else if (obedit->type == OB_ARMATURE) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Joints:%s/%s | Bones:%s/%s"), - stats_fmt->totvertsel, - stats_fmt->totvert, - stats_fmt->totbonesel, - stats_fmt->totbone); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Joints:%s/%s | Bones:%s/%s"), + stats_fmt->totvertsel, + stats_fmt->totvert, + stats_fmt->totbonesel, + stats_fmt->totbone); } else { - *ofs += BLI_snprintf( + *ofs += BLI_snprintf_rlen( info + *ofs, len - *ofs, TIP_("Verts:%s/%s"), stats_fmt->totvertsel, stats_fmt->totvert); } } else if (ob && (object_mode & OB_MODE_POSE)) { - *ofs += BLI_snprintf( + *ofs += BLI_snprintf_rlen( info + *ofs, len - *ofs, TIP_("Bones:%s/%s"), stats_fmt->totbonesel, stats_fmt->totbone); } else if ((ob) && (ob->type == OB_GPENCIL)) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"), - stats_fmt->totgplayer, - stats_fmt->totgpframe, - stats_fmt->totgpstroke, - stats_fmt->totgppoint); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"), + stats_fmt->totgplayer, + stats_fmt->totgpframe, + stats_fmt->totgpstroke, + stats_fmt->totgppoint); } else if (ob && (object_mode & OB_MODE_SCULPT)) { if (stats_is_object_dynamic_topology_sculpt(ob)) { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Verts:%s | Tris:%s"), - stats_fmt->totvert, - stats_fmt->tottri); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Verts:%s | Tris:%s"), + stats_fmt->totvert, + stats_fmt->tottri); } else { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Verts:%s/%s | Faces:%s/%s"), - stats_fmt->totvertsculpt, - stats_fmt->totvert, - stats_fmt->totfacesculpt, - stats_fmt->totface); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Verts:%s/%s | Faces:%s/%s"), + stats_fmt->totvertsculpt, + stats_fmt->totvert, + stats_fmt->totfacesculpt, + stats_fmt->totface); } } else { - *ofs += BLI_snprintf(info + *ofs, - len - *ofs, - TIP_("Verts:%s | Faces:%s | Tris:%s"), - stats_fmt->totvert, - stats_fmt->totface, - stats_fmt->tottri); + *ofs += BLI_snprintf_rlen(info + *ofs, + len - *ofs, + TIP_("Verts:%s | Faces:%s | Tris:%s"), + stats_fmt->totvert, + stats_fmt->totface, + stats_fmt->tottri); } - *ofs += BLI_snprintf( + *ofs += BLI_snprintf_rlen( info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj); } @@ -613,11 +613,11 @@ static const char *info_statusbar_string(Main *bmain, /* Memory status. */ if (statusbar_flag & STATUSBAR_SHOW_MEMORY) { if (info[0]) { - ofs += BLI_snprintf(info + ofs, len - ofs, " | "); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | "); } uintptr_t mem_in_use = MEM_get_memory_in_use(); BLI_str_format_byte_unit(formatted_mem, mem_in_use, false); - ofs += BLI_snprintf(info + ofs, len, TIP_("Memory: %s"), formatted_mem); + ofs += BLI_snprintf_rlen(info + ofs, len, TIP_("Memory: %s"), formatted_mem); } /* GPU VRAM status. */ @@ -627,27 +627,27 @@ static const char *info_statusbar_string(Main *bmain, float gpu_total_gb = gpu_tot_mem_kb / 1048576.0f; float gpu_free_gb = gpu_free_mem_kb / 1048576.0f; if (info[0]) { - ofs += BLI_snprintf(info + ofs, len - ofs, " | "); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | "); } if (gpu_free_mem_kb && gpu_tot_mem_kb) { - ofs += BLI_snprintf(info + ofs, - len - ofs, - TIP_("VRAM: %.1f/%.1f GiB"), - gpu_total_gb - gpu_free_gb, - gpu_total_gb); + ofs += BLI_snprintf_rlen(info + ofs, + len - ofs, + TIP_("VRAM: %.1f/%.1f GiB"), + gpu_total_gb - gpu_free_gb, + gpu_total_gb); } else { /* Can only show amount of GPU VRAM available. */ - ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb); } } /* Blender version. */ if (statusbar_flag & STATUSBAR_SHOW_VERSION) { if (info[0]) { - ofs += BLI_snprintf(info + ofs, len - ofs, " | "); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | "); } - ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string()); + ofs += BLI_snprintf_rlen(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string()); } return info; diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 35b5c6f7f5d..350be247014 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -533,20 +533,21 @@ void headerRotation(TransInfo *t, char *str, const int str_size, float final) outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, str_size - ofs, TIP_("Rotation: %s %s %s"), &c[0], t->con.text, t->proptext); } else { - ofs += BLI_snprintf(str + ofs, - str_size - ofs, - TIP_("Rotation: %.2f%s %s"), - RAD2DEGF(final), - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Rotation: %.2f%s %s"), + RAD2DEGF(final), + t->con.text, + t->proptext); } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf(str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf_rlen( + str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } } @@ -822,54 +823,55 @@ void headerResize(TransInfo *t, const float vec[3], char *str, const int str_siz if (t->con.mode & CON_APPLY) { switch (t->num.idx_max) { case 0: - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, str_size - ofs, TIP_("Scale: %s%s %s"), &tvec[0], t->con.text, t->proptext); break; case 1: - ofs += BLI_snprintf(str + ofs, - str_size - ofs, - TIP_("Scale: %s : %s%s %s"), - &tvec[0], - &tvec[NUM_STR_REP_LEN], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Scale: %s : %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + t->con.text, + t->proptext); break; case 2: - ofs += BLI_snprintf(str + ofs, - str_size - ofs, - TIP_("Scale: %s : %s : %s%s %s"), - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Scale: %s : %s : %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + t->con.text, + t->proptext); break; } } else { if (t->flag & T_2D_EDIT) { - ofs += BLI_snprintf(str + ofs, - str_size - ofs, - TIP_("Scale X: %s Y: %s%s %s"), - &tvec[0], - &tvec[NUM_STR_REP_LEN], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Scale X: %s Y: %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + t->con.text, + t->proptext); } else { - ofs += BLI_snprintf(str + ofs, - str_size - ofs, - TIP_("Scale X: %s Y: %s Z: %s%s %s"), - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + str_size - ofs, + TIP_("Scale X: %s Y: %s Z: %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + t->con.text, + t->proptext); } } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf(str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf_rlen( + str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } } diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c index 4330d5e79be..7e7b79c9f90 100644 --- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c @@ -72,7 +72,7 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.0f, %.0f", val[0], val[1]); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text); const wmKeyMapItem *kmi = t->custom.mode.data; @@ -80,10 +80,10 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA ofs += WM_keymap_item_to_string(kmi, false, str + ofs, UI_MAX_DRAW_STR - ofs); } - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_(" or Alt) Expand to fit %s"), - WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + TIP_(" or Alt) Expand to fit %s"), + WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); } static void applySeqSlideValue(TransInfo *t, const float val[2]) diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index 16c1c05a6f8..d255a7d5660 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -1482,15 +1482,15 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs); } else { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even)); if (use_even) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped)); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp)); /* done with header string */ diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c index 6e497d85417..d2d73a14396 100644 --- a/source/blender/editors/transform/transform_mode_shrink_fatten.c +++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c @@ -79,7 +79,7 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; outputNumInput(&(t->num), c, unit); - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%s", c); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%s", c); } else { /* default header print */ @@ -93,12 +93,12 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) true); } else { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f", distance); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f", distance); } } if (t->proptext[0]) { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %s", t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, " %s", t->proptext); } ofs += BLI_strncpy_rlen(str + ofs, ", (", sizeof(str) - ofs); diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c index 5ad6d04b4de..948242e547f 100644 --- a/source/blender/editors/transform/transform_mode_timetranslate.c +++ b/source/blender/editors/transform/transform_mode_timetranslate.c @@ -76,10 +76,10 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) } } - ofs += BLI_snprintf(str, UI_MAX_DRAW_STR, TIP_("DeltaX: %s"), &tvec[0]); + ofs += BLI_snprintf_rlen(str, UI_MAX_DRAW_STR, TIP_("DeltaX: %s"), &tvec[0]); if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } } diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c index 5a57a69f986..d05077ef1ef 100644 --- a/source/blender/editors/transform/transform_mode_trackball.c +++ b/source/blender/editors/transform/transform_mode_trackball.c @@ -102,24 +102,24 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_snprintf(str + ofs, - sizeof(str) - ofs, - TIP_("Trackball: %s %s %s"), - &c[0], - &c[NUM_STR_REP_LEN], - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + sizeof(str) - ofs, + TIP_("Trackball: %s %s %s"), + &c[0], + &c[NUM_STR_REP_LEN], + t->proptext); } else { - ofs += BLI_snprintf(str + ofs, - sizeof(str) - ofs, - TIP_("Trackball: %.2f %.2f %s"), - RAD2DEGF(phi[0]), - RAD2DEGF(phi[1]), - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + sizeof(str) - ofs, + TIP_("Trackball: %.2f %.2f %s"), + RAD2DEGF(phi[0]), + RAD2DEGF(phi[1]), + t->proptext); } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 175b7b52a1a..3088f6a7776 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -141,67 +141,67 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ if (t->con.mode & CON_APPLY) { switch (t->num.idx_max) { case 0: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "D: %s (%s)%s %s %s", - &tvec[0], - distvec, - t->con.text, - t->proptext, - autoik); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "D: %s (%s)%s %s %s", + &tvec[0], + distvec, + t->con.text, + t->proptext, + autoik); break; case 1: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "D: %s D: %s (%s)%s %s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - distvec, - t->con.text, - t->proptext, - autoik); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "D: %s D: %s (%s)%s %s %s", + &tvec[0], + &tvec[NUM_STR_REP_LEN], + distvec, + t->con.text, + t->proptext, + autoik); break; case 2: - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "D: %s D: %s D: %s (%s)%s %s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - distvec, - t->con.text, - t->proptext, - autoik); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "D: %s D: %s D: %s (%s)%s %s %s", + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + distvec, + t->con.text, + t->proptext, + autoik); break; } } else { if (t->flag & T_2D_EDIT) { - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "Dx: %s Dy: %s (%s)%s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - distvec, - t->con.text, - t->proptext); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "Dx: %s Dy: %s (%s)%s %s", + &tvec[0], + &tvec[NUM_STR_REP_LEN], + distvec, + t->con.text, + t->proptext); } else { - ofs += BLI_snprintf(str + ofs, - UI_MAX_DRAW_STR - ofs, - "Dx: %s Dy: %s Dz: %s (%s)%s %s %s", - &tvec[0], - &tvec[NUM_STR_REP_LEN], - &tvec[NUM_STR_REP_LEN * 2], - distvec, - t->con.text, - t->proptext, - autoik); + ofs += BLI_snprintf_rlen(str + ofs, + UI_MAX_DRAW_STR - ofs, + "Dx: %s Dy: %s Dz: %s (%s)%s %s %s", + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + distvec, + t->con.text, + t->proptext, + autoik); } } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); } @@ -217,12 +217,12 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ WM_modalkeymap_items_to_string( t->keymap, TFM_MODAL_INSERTOFS_TOGGLE_DIR, true, str_km, sizeof(str_km)); - ofs += BLI_snprintf(str, - UI_MAX_DRAW_STR, - TIP_("Auto-offset set to %s - press %s to toggle direction | %s"), - str_dir, - str_km, - str_old); + ofs += BLI_snprintf_rlen(str, + UI_MAX_DRAW_STR, + TIP_("Auto-offset set to %s - press %s to toggle direction | %s"), + str_dir, + str_km, + str_old); MEM_freeN((void *)str_old); } diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c index 1e5d027e253..e16aa636872 100644 --- a/source/blender/editors/transform/transform_mode_vert_slide.c +++ b/source/blender/editors/transform/transform_mode_vert_slide.c @@ -606,15 +606,15 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs); } else { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final); + ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even)); if (use_even) { - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped)); } - ofs += BLI_snprintf( + ofs += BLI_snprintf_rlen( str + ofs, sizeof(str) - ofs, TIP_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp)); /* done with header string */ diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 440375f60dc..48b5b0c34db 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -516,7 +516,8 @@ static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf) * The first "Blender" is a simple identify to help * in the read process. */ - text_len = BLI_snprintf(text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop)); + text_len = BLI_snprintf_rlen( + text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop)); jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *)text, text_len + 1); /* TODO(sergey): Ideally we will try to re-use allocation as diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index c312012b24c..5d38a3692c3 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -2253,7 +2253,7 @@ static PyObject *Matrix_str(MatrixObject *self) for (col = 0; col < self->num_col; col++) { maxsize[col] = 0; for (row = 0; row < self->num_row; row++) { - const int size = BLI_snprintf( + const int size = BLI_snprintf_rlen( dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col)); maxsize[col] = max_ii(maxsize[col], size); } -- cgit v1.2.3 From fd0bb24e4a15f18c1ec8904541baea1fc3beb04c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 27 May 2021 17:44:30 +1000 Subject: Cleanup: simplify logic for copying vector button as text An arbitrary size offsets was used in float_array_to_string, simplify the loop, use exact size limits. Also rename variables so it's clear which array the length apply to. --- .../blender/editors/interface/interface_handlers.c | 65 ++++++++-------------- 1 file changed, 24 insertions(+), 41 deletions(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 22a2e0a55d1..5f98a501bec 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2346,16 +2346,16 @@ static int get_but_property_array_length(uiBut *but) } static void ui_but_set_float_array( - bContext *C, uiBut *but, uiHandleButtonData *data, float *values, int array_length) + bContext *C, uiBut *but, uiHandleButtonData *data, const float *values, const int values_len) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - for (int i = 0; i < array_length; i++) { + for (int i = 0; i < values_len; i++) { RNA_property_float_set_index(&but->rnapoin, but->rnaprop, i, values[i]); } if (data) { if (but->type == UI_BTYPE_UNITVEC) { - BLI_assert(array_length == 3); + BLI_assert(values_len == 3); copy_v3_v3(data->vec, values); } else { @@ -2366,56 +2366,39 @@ static void ui_but_set_float_array( button_activate_state(C, but, BUTTON_STATE_EXIT); } -static void float_array_to_string(float *values, - int array_length, +static void float_array_to_string(const float *values, + const int values_len, char *output, int output_len_max) { - /* to avoid buffer overflow attacks; numbers are quite arbitrary */ - BLI_assert(output_len_max > 15); - output_len_max -= 10; - - int current_index = 0; - output[current_index] = '['; - current_index++; - - for (int i = 0; i < array_length; i++) { - int length = BLI_snprintf_rlen( - output + current_index, output_len_max - current_index, "%f", values[i]); - current_index += length; - - if (i < array_length - 1) { - if (current_index < output_len_max) { - output[current_index + 0] = ','; - output[current_index + 1] = ' '; - current_index += 2; - } - } + const int values_end = values_len - 1; + int ofs = 0; + output[ofs++] = '['; + for (int i = 0; i < values_len; i++) { + ofs += BLI_snprintf_rlen( + output + ofs, output_len_max - ofs, (i != values_end) ? "%f, " : "%f]", values[i]); } - - output[current_index + 0] = ']'; - output[current_index + 1] = '\0'; } static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max) { - const int array_length = get_but_property_array_length(but); - float *values = alloca(array_length * sizeof(float)); + const int values_len = get_but_property_array_length(but); + float *values = alloca(values_len * sizeof(float)); RNA_property_float_get_array(&but->rnapoin, but->rnaprop, values); - float_array_to_string(values, array_length, output, output_len_max); + float_array_to_string(values, values_len, output, output_len_max); } -static bool parse_float_array(char *text, float *values, int expected_length) +static bool parse_float_array(char *text, float *values, int values_len_expected) { /* can parse max 4 floats for now */ - BLI_assert(0 <= expected_length && expected_length <= 4); + BLI_assert(0 <= values_len_expected && values_len_expected <= 4); float v[5]; - const int actual_length = sscanf( + const int values_len_actual = sscanf( text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]); - if (actual_length == expected_length) { - memcpy(values, v, sizeof(float) * expected_length); + if (values_len_actual == values_len_expected) { + memcpy(values, v, sizeof(float) * values_len_expected); return true; } return false; @@ -2426,16 +2409,16 @@ static void ui_but_paste_numeric_array(bContext *C, uiHandleButtonData *data, char *buf_paste) { - const int array_length = get_but_property_array_length(but); - if (array_length > 4) { + const int values_len = get_but_property_array_length(but); + if (values_len > 4) { /* not supported for now */ return; } - float *values = alloca(sizeof(float) * array_length); + float *values = alloca(sizeof(float) * values_len); - if (parse_float_array(buf_paste, values, array_length)) { - ui_but_set_float_array(C, but, data, values, array_length); + if (parse_float_array(buf_paste, values, values_len)) { + ui_but_set_float_array(C, but, data, values, values_len); } else { WM_report(RPT_ERROR, "Expected an array of numbers: [n, n, ...]"); -- cgit v1.2.3 From ffa2a1771e05aff445f3c9c48ba7f2a20258119d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 27 May 2021 10:41:40 +0200 Subject: Cleanup: inconsistent parameter name --- source/blender/editors/transform/transform_mode.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index 874a5ebc0a8..a2b95eb3de4 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -47,7 +47,7 @@ void protectedTransBits(short protectflag, float vec[3]); void protectedSizeBits(short protectflag, float size[3]); void constraintTransLim(TransInfo *t, TransData *td); void constraintSizeLim(TransInfo *t, TransData *td); -void headerRotation(TransInfo *t, char *str, int str_len, float final); +void headerRotation(TransInfo *t, char *str, int str_size, float final); void ElementRotation_ex(TransInfo *t, TransDataContainer *tc, TransData *td, @@ -55,7 +55,7 @@ void ElementRotation_ex(TransInfo *t, const float *center); void ElementRotation( TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around); -void headerResize(TransInfo *t, const float vec[3], char *str, int str_len); +void headerResize(TransInfo *t, const float vec[3], char *str, int str_size); void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]); short getAnimEdit_SnapMode(TransInfo *t); void doAnimEdit_SnapFrame( -- cgit v1.2.3 From 46e1ad711ce7b28c4c7b6cd81f2024dd63acda67 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 27 May 2021 10:46:43 +0200 Subject: Cleanup: inconsistent parameter name --- source/blender/gpu/GPU_index_buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h index bdacfe6fc0f..8362dbcaccc 100644 --- a/source/blender/gpu/GPU_index_buffer.h +++ b/source/blender/gpu/GPU_index_buffer.h @@ -87,7 +87,7 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, * local copy, use `GPU_indexbuf_unmap` after calling `GPU_indexbuf_read`. */ const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem); -uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_data); +uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer); void GPU_indexbuf_discard(GPUIndexBuf *elem); -- cgit v1.2.3 From 06d48367fae6e29426d889081acbe95de0ab663d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 27 May 2021 10:47:02 +0200 Subject: Fix (studio-reported) infinite loop in resync code. Very stupid mistake in libraries indirect-level building code, was not skipping 'loop-back' ID pointers. Note that we also need some level of checks for the case where there would be an actual dependency loop between libraries, this is not supposed to be possible, but better be safe than sorry. Will add in next commit. --- source/blender/blenkernel/intern/lib_override.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 1aa94f82eb3..e16a9081b35 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1279,6 +1279,9 @@ bool BKE_lib_override_library_resync(Main *bmain, static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) { + if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) { + return IDWALK_RET_NOP; + } ID *id_owner = cb_data->id_owner; ID *id = *cb_data->id_pointer; if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) { -- cgit v1.2.3 From bf7a67c5c63d02f1736dcb254c493620c5d98d91 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 27 May 2021 10:42:22 +0200 Subject: Refactor: Rename pass accessor in viewport parameters No need to state that it is a viewport display pass, since the method is within viewport parameters it is implied that parameters do belong to the viewport. Brings this code closer to the Cycles-X branch. --- intern/cycles/blender/blender_sync.cpp | 3 +-- intern/cycles/blender/blender_viewport.cpp | 5 +++-- intern/cycles/blender/blender_viewport.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index bacf57df48e..e6e0fb54457 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -233,8 +233,7 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d if (!has_updates_) { Film *film = scene->film; - const PassType new_display_pass = new_viewport_parameters.get_viewport_display_render_pass( - b_v3d); + const PassType new_display_pass = new_viewport_parameters.get_render_pass(b_v3d); has_updates_ |= film->get_display_pass() != new_display_pass; } } diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index aac3d41314a..99dbde91858 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "blender_viewport.h" #include "blender_util.h" @@ -70,7 +71,7 @@ const bool BlenderViewportParameters::custom_viewport_parameters() const return !(use_scene_world && use_scene_lights); } -PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceView3D &b_v3d) +PassType BlenderViewportParameters::get_render_pass(BL::SpaceView3D &b_v3d) { PassType display_pass = PASS_NONE; if (b_v3d) { @@ -84,7 +85,7 @@ PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceVi PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector &passes) { if (b_v3d) { - PassType display_pass = BlenderViewportParameters::get_viewport_display_render_pass(b_v3d); + PassType display_pass = BlenderViewportParameters::get_render_pass(b_v3d); passes.clear(); Pass::add(display_pass, passes); diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index 7c6c9c4d274..5939076979a 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -44,9 +44,9 @@ class BlenderViewportParameters { friend class BlenderSync; public: - /* Retrieve the render pass that needs to be displayed on the given `SpaceView3D` + /* Retrieve the render pass type that needs to be displayed on the given `SpaceView3D` * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */ - static PassType get_viewport_display_render_pass(BL::SpaceView3D &b_v3d); + static PassType get_render_pass(BL::SpaceView3D &b_v3d); }; PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector &passes); -- cgit v1.2.3 From 4ea77429736737bf074d4294276e4cdcfce87ff9 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 27 May 2021 11:07:58 +0200 Subject: Refactor: Remove friend class from Cycles viewport parameters Such pattern should only be used when it is really needed. Otherwise just stick to a more regular design, without worrying who is the user of the class. Otherwise it will be annoying to subclass or unit test. --- intern/cycles/blender/blender_viewport.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index 5939076979a..08ef6a9e679 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -28,7 +28,7 @@ CCL_NAMESPACE_BEGIN class BlenderViewportParameters { - private: + public: bool use_scene_world; bool use_scene_lights; float studiolight_rotate_z; @@ -37,13 +37,11 @@ class BlenderViewportParameters { ustring studiolight_path; BlenderViewportParameters(); - BlenderViewportParameters(BL::SpaceView3D &b_v3d); + explicit BlenderViewportParameters(BL::SpaceView3D &b_v3d); const bool modified(const BlenderViewportParameters &other) const; const bool custom_viewport_parameters() const; - friend class BlenderSync; - public: /* Retrieve the render pass type that needs to be displayed on the given `SpaceView3D` * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */ static PassType get_render_pass(BL::SpaceView3D &b_v3d); -- cgit v1.2.3 From 8f43e31a7195b35b73d662775b4105a880d52a57 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 27 May 2021 11:10:37 +0200 Subject: Cleanup: const qualifier of return type --- intern/cycles/blender/blender_viewport.cpp | 4 ++-- intern/cycles/blender/blender_viewport.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index 99dbde91858..dbfee33fd59 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -57,7 +57,7 @@ BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) } /* Check if two instances are different. */ -const bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const +bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const { return use_scene_world != other.use_scene_world || use_scene_lights != other.use_scene_lights || studiolight_rotate_z != other.studiolight_rotate_z || @@ -66,7 +66,7 @@ const bool BlenderViewportParameters::modified(const BlenderViewportParameters & studiolight_path != other.studiolight_path; } -const bool BlenderViewportParameters::custom_viewport_parameters() const +bool BlenderViewportParameters::custom_viewport_parameters() const { return !(use_scene_world && use_scene_lights); } diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index 08ef6a9e679..e73399ea6db 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -39,8 +39,8 @@ class BlenderViewportParameters { BlenderViewportParameters(); explicit BlenderViewportParameters(BL::SpaceView3D &b_v3d); - const bool modified(const BlenderViewportParameters &other) const; - const bool custom_viewport_parameters() const; + bool modified(const BlenderViewportParameters &other) const; + bool custom_viewport_parameters() const; /* Retrieve the render pass type that needs to be displayed on the given `SpaceView3D` * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */ -- cgit v1.2.3 From 15df83e9c910f620e4d1ab24f7175c7a2eae57b3 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 27 May 2021 11:11:49 +0200 Subject: Cleanup: Use logical OR in Cycles background shader --- intern/cycles/blender/blender_shader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 736c10e8881..bad7fee3032 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -1501,7 +1501,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, background->set_transparent_roughness_threshold(0.0f); } - background->set_use_shader(view_layer.use_background_shader | + background->set_use_shader(view_layer.use_background_shader || viewport_parameters.custom_viewport_parameters()); background->set_use_ao(background->get_use_ao() && view_layer.use_background_ao); -- cgit v1.2.3 From 616b071c4fa42f4b412ac12aa41120f41b79ec51 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 27 May 2021 11:16:40 +0200 Subject: Refactor: Naming in Cycles viewport methods Makes it more explicit they operate on shading/light. Gives room to move more viewport related settings into this class and cover with specific or generic modification checks. --- intern/cycles/blender/blender_shader.cpp | 4 ++-- intern/cycles/blender/blender_sync.cpp | 2 +- intern/cycles/blender/blender_viewport.cpp | 5 ++--- intern/cycles/blender/blender_viewport.h | 9 +++++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index bad7fee3032..7f129310736 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -1373,7 +1373,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, BlenderViewportParameters new_viewport_parameters(b_v3d); if (world_recalc || update_all || b_world.ptr.data != world_map || - viewport_parameters.modified(new_viewport_parameters)) { + viewport_parameters.shader_modified(new_viewport_parameters)) { Shader *shader = scene->default_background; ShaderGraph *graph = new ShaderGraph(); @@ -1502,7 +1502,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, } background->set_use_shader(view_layer.use_background_shader || - viewport_parameters.custom_viewport_parameters()); + viewport_parameters.use_custom_shader()); background->set_use_ao(background->get_use_ao() && view_layer.use_background_ao); background->tag_update(scene); diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index e6e0fb54457..0d2b2641312 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -225,7 +225,7 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d if (b_v3d) { BlenderViewportParameters new_viewport_parameters(b_v3d); - if (viewport_parameters.modified(new_viewport_parameters)) { + if (viewport_parameters.shader_modified(new_viewport_parameters)) { world_recalc = true; has_updates_ = true; } diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index dbfee33fd59..6cd815d337b 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -56,8 +56,7 @@ BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) } } -/* Check if two instances are different. */ -bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const +bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters &other) const { return use_scene_world != other.use_scene_world || use_scene_lights != other.use_scene_lights || studiolight_rotate_z != other.studiolight_rotate_z || @@ -66,7 +65,7 @@ bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) studiolight_path != other.studiolight_path; } -bool BlenderViewportParameters::custom_viewport_parameters() const +bool BlenderViewportParameters::use_custom_shader() const { return !(use_scene_world && use_scene_lights); } diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index e73399ea6db..c588a5b8f87 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -29,6 +29,7 @@ CCL_NAMESPACE_BEGIN class BlenderViewportParameters { public: + /* Shader. */ bool use_scene_world; bool use_scene_lights; float studiolight_rotate_z; @@ -39,8 +40,12 @@ class BlenderViewportParameters { BlenderViewportParameters(); explicit BlenderViewportParameters(BL::SpaceView3D &b_v3d); - bool modified(const BlenderViewportParameters &other) const; - bool custom_viewport_parameters() const; + /* Check whether any of shading related settings are different from the given parameters. */ + bool shader_modified(const BlenderViewportParameters &other) const; + + /* Returns truth when a custom shader defined by the viewport is to be used instead of the + * regular background shader or scene light. */ + bool use_custom_shader() const; /* Retrieve the render pass type that needs to be displayed on the given `SpaceView3D` * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */ -- cgit v1.2.3 From 7b5f4c88370ed2752d398a8d8c5aca4b4435a0c9 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 27 May 2021 11:24:05 +0200 Subject: Cleanup: Redundnat member init in Cycles viewport parameters --- intern/cycles/blender/blender_viewport.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index 6cd815d337b..60de2015a67 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -25,8 +25,7 @@ BlenderViewportParameters::BlenderViewportParameters() use_scene_lights(true), studiolight_rotate_z(0.0f), studiolight_intensity(1.0f), - studiolight_background_alpha(1.0f), - studiolight_path(ustring()) + studiolight_background_alpha(1.0f) { } -- cgit v1.2.3 From 2414a5787d0c7a03e89d5087b8d4d5ae8e1d27c8 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 27 May 2021 11:31:03 +0200 Subject: Refactor: Move display pass to Cycles viewport parameters Allows to centralize storage and modification checks in a single place, avoiding duplication in the synchronization code. Ideally we would somehow be able to more granularly modify Cycles side objects. Leaving this for a future decision, because it might be better to implement it as a graph on the sync side. --- intern/cycles/blender/blender_sync.cpp | 7 +--- intern/cycles/blender/blender_viewport.cpp | 51 ++++++++++++++++-------------- intern/cycles/blender/blender_viewport.h | 13 +++++--- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 0d2b2641312..82b3abd4432 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -230,12 +230,7 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d has_updates_ = true; } - if (!has_updates_) { - Film *film = scene->film; - - const PassType new_display_pass = new_viewport_parameters.get_render_pass(b_v3d); - has_updates_ |= film->get_display_pass() != new_display_pass; - } + has_updates_ |= viewport_parameters.modified(new_viewport_parameters); } } diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp index 60de2015a67..07408fee218 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/blender_viewport.cpp @@ -25,7 +25,8 @@ BlenderViewportParameters::BlenderViewportParameters() use_scene_lights(true), studiolight_rotate_z(0.0f), studiolight_intensity(1.0f), - studiolight_background_alpha(1.0f) + studiolight_background_alpha(1.0f), + display_pass(PASS_COMBINED) { } @@ -37,22 +38,24 @@ BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) } BL::View3DShading shading = b_v3d.shading(); + PointerRNA cshading = RNA_pointer_get(&shading.ptr, "cycles"); - /* We only copy the parameters if we are in look dev mode. otherwise + /* We only copy the shading parameters if we are in look dev mode. otherwise * defaults are being used. These defaults mimic normal render settings */ - if (shading.type() != BL::View3DShading::type_RENDERED) { - return; + if (shading.type() == BL::View3DShading::type_RENDERED) { + use_scene_world = shading.use_scene_world_render(); + use_scene_lights = shading.use_scene_lights_render(); + + if (!use_scene_world) { + studiolight_rotate_z = shading.studiolight_rotate_z(); + studiolight_intensity = shading.studiolight_intensity(); + studiolight_background_alpha = shading.studiolight_background_alpha(); + studiolight_path = shading.selected_studio_light().path(); + } } - use_scene_world = shading.use_scene_world_render(); - use_scene_lights = shading.use_scene_lights_render(); - - if (!use_scene_world) { - studiolight_rotate_z = shading.studiolight_rotate_z(); - studiolight_intensity = shading.studiolight_intensity(); - studiolight_background_alpha = shading.studiolight_background_alpha(); - studiolight_path = shading.selected_studio_light().path(); - } + /* Film. */ + display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1); } bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters &other) const @@ -64,26 +67,26 @@ bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters studiolight_path != other.studiolight_path; } -bool BlenderViewportParameters::use_custom_shader() const +bool BlenderViewportParameters::film_modified(const BlenderViewportParameters &other) const { - return !(use_scene_world && use_scene_lights); + return display_pass != other.display_pass; } -PassType BlenderViewportParameters::get_render_pass(BL::SpaceView3D &b_v3d) +bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const { - PassType display_pass = PASS_NONE; - if (b_v3d) { - BL::View3DShading b_view3dshading = b_v3d.shading(); - PointerRNA cshading = RNA_pointer_get(&b_view3dshading.ptr, "cycles"); - display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1); - } - return display_pass; + return shader_modified(other) || film_modified(other); +} + +bool BlenderViewportParameters::use_custom_shader() const +{ + return !(use_scene_world && use_scene_lights); } PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector &passes) { if (b_v3d) { - PassType display_pass = BlenderViewportParameters::get_render_pass(b_v3d); + const BlenderViewportParameters viewport_parameters(b_v3d); + const PassType display_pass = viewport_parameters.display_pass; passes.clear(); Pass::add(display_pass, passes); diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index c588a5b8f87..0dfb3918852 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -37,19 +37,24 @@ class BlenderViewportParameters { float studiolight_background_alpha; ustring studiolight_path; + /* Film. */ + PassType display_pass; + BlenderViewportParameters(); explicit BlenderViewportParameters(BL::SpaceView3D &b_v3d); /* Check whether any of shading related settings are different from the given parameters. */ bool shader_modified(const BlenderViewportParameters &other) const; + /* Check whether any of film related settings are different from the given parameters. */ + bool film_modified(const BlenderViewportParameters &other) const; + + /* Check whether any of settings are different from the given parameters. */ + bool modified(const BlenderViewportParameters &other) const; + /* Returns truth when a custom shader defined by the viewport is to be used instead of the * regular background shader or scene light. */ bool use_custom_shader() const; - - /* Retrieve the render pass type that needs to be displayed on the given `SpaceView3D` - * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */ - static PassType get_render_pass(BL::SpaceView3D &b_v3d); }; PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector &passes); -- cgit v1.2.3 From ac2266fe57bc04845f61bf131cd416797721eff9 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 27 May 2021 11:35:38 +0200 Subject: Cleanup: Unused include in Cycles --- intern/cycles/blender/blender_viewport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h index 0dfb3918852..d6518597053 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/blender_viewport.h @@ -18,12 +18,12 @@ #define __BLENDER_VIEWPORT_H__ #include "MEM_guardedalloc.h" + #include "RNA_access.h" #include "RNA_blender_cpp.h" #include "RNA_types.h" #include "render/film.h" -#include "util/util_param.h" CCL_NAMESPACE_BEGIN -- cgit v1.2.3 From 42fba7f0d23901ff10ad8d076901decc0fb97d56 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 27 May 2021 11:53:44 +0200 Subject: LibOverride: Add heuristic protection against infinite loop due to libraries inter-dependencies. This is not supposed to happen, but better be safe than sorry, and assume it is beyond unlikely that someone would use chains of over 10k linked libraries. --- source/blender/blenkernel/intern/lib_override.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index e16a9081b35..aa7dfdf2bfa 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1286,6 +1286,17 @@ static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) ID *id = *cb_data->id_pointer; if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) { const int owner_library_indirect_level = id_owner->lib != NULL ? id_owner->lib->temp_index : 0; + if (owner_library_indirect_level > 10000) { + CLOG_ERROR( + &LOG, + "Levels of indirect usages of libraries is way too high, skipping further building " + "loops (Involves at least '%s' and '%s')", + id_owner->lib->filepath, + id->lib->filepath); + BLI_assert(0); + return IDWALK_RET_NOP; + } + if (owner_library_indirect_level >= id->lib->temp_index) { id->lib->temp_index = owner_library_indirect_level + 1; *(bool *)cb_data->user_data = true; -- cgit v1.2.3 From f45270b4fb88c9d53b3b5abf0e3f2d6964623edf Mon Sep 17 00:00:00 2001 From: YimingWu Date: Thu, 27 May 2021 18:32:43 +0800 Subject: Cleanup: Line art variable naming. Change `reln` to `eln`. Reviewed By: Sebastian Parborg (zeddb) Differential Revision: https://developer.blender.org/D11411 --- .../gpencil_modifiers/intern/lineart/MOD_lineart.h | 2 +- .../gpencil_modifiers/intern/lineart/lineart_cpu.c | 102 ++++++++++----------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index e679dce2f2d..4e0585c9f6d 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -155,7 +155,7 @@ typedef struct LineartEdge { /** * Still need this entry because culled lines will not add to object - * #LineartElementLinkNode node (known as `reln` internally). + * #LineartElementLinkNode node (known as `eln` internally). * * TODO: If really need more savings, we can allocate this in a "extended" way too, but we need * another bit in flags to be able to show the difference. diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index ae8157e1a97..24c405f1d67 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -629,55 +629,55 @@ static bool lineart_point_inside_triangle3d(double v[3], double v0[3], double v1 */ static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBuffer *rb) { - LineartElementLinkNode *reln; + LineartElementLinkNode *eln; /* We don't need to allocate a whole bunch of triangles because the amount of clipped triangles * are relatively small. */ LineartTriangle *render_triangles = lineart_mem_acquire(&rb->render_data_pool, 64 * rb->triangle_size); - reln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers, - &rb->render_data_pool, - render_triangles, - sizeof(LineartElementLinkNode)); - reln->element_count = 64; - reln->flags |= LRT_ELEMENT_IS_ADDITIONAL; + eln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers, + &rb->render_data_pool, + render_triangles, + sizeof(LineartElementLinkNode)); + eln->element_count = 64; + eln->flags |= LRT_ELEMENT_IS_ADDITIONAL; - return reln; + return eln; } static LineartElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer *rb) { - LineartElementLinkNode *reln; + LineartElementLinkNode *eln; LineartVert *render_vertices = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartVert) * 64); - reln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers, - &rb->render_data_pool, - render_vertices, - sizeof(LineartElementLinkNode)); - reln->element_count = 64; - reln->flags |= LRT_ELEMENT_IS_ADDITIONAL; + eln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers, + &rb->render_data_pool, + render_vertices, + sizeof(LineartElementLinkNode)); + eln->element_count = 64; + eln->flags |= LRT_ELEMENT_IS_ADDITIONAL; - return reln; + return eln; } static LineartElementLinkNode *lineart_memory_get_edge_space(LineartRenderBuffer *rb) { - LineartElementLinkNode *reln; + LineartElementLinkNode *eln; LineartEdge *render_edges = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * 64); - reln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers, - &rb->render_data_pool, - render_edges, - sizeof(LineartElementLinkNode)); - reln->element_count = 64; - reln->crease_threshold = rb->crease_threshold; - reln->flags |= LRT_ELEMENT_IS_ADDITIONAL; + eln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers, + &rb->render_data_pool, + render_edges, + sizeof(LineartElementLinkNode)); + eln->element_count = 64; + eln->crease_threshold = rb->crease_threshold; + eln->flags |= LRT_ELEMENT_IS_ADDITIONAL; - return reln; + return eln; } static void lineart_triangle_post(LineartTriangle *tri, LineartTriangle *orig) @@ -1254,14 +1254,14 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) } /* Then go through all the other triangles. */ - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) { - if (reln->flags & LRT_ELEMENT_IS_ADDITIONAL) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + if (eln->flags & LRT_ELEMENT_IS_ADDITIONAL) { continue; } - ob = reln->object_ref; - for (i = 0; i < reln->element_count; i++) { + ob = eln->object_ref; + for (i = 0; i < eln->element_count; i++) { /* Select the triangle in the array. */ - tri = (void *)(((uchar *)reln->pointer) + rb->triangle_size * i); + tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i); LRT_CULL_DECIDE_INSIDE LRT_CULL_ENSURE_MEMORY @@ -1300,10 +1300,10 @@ static void lineart_main_free_adjacent_data(LineartRenderBuffer *rb) while ((ld = BLI_pophead(&rb->triangle_adjacent_pointers)) != NULL) { MEM_freeN(ld->data); } - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) { - LineartTriangle *tri = reln->pointer; + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + LineartTriangle *tri = eln->pointer; int i; - for (i = 0; i < reln->element_count; i++) { + for (i = 0; i < eln->element_count; i++) { /* See definition of tri->intersecting_verts and the usage in * lineart_geometry_object_load() for detailed. */ tri->intersecting_verts = NULL; @@ -1321,9 +1321,9 @@ static void lineart_main_perspective_division(LineartRenderBuffer *rb) return; } - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->vertex_buffer_pointers) { - vt = reln->pointer; - for (i = 0; i < reln->element_count; i++) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->vertex_buffer_pointers) { + vt = eln->pointer; + for (i = 0; i < eln->element_count; i++) { /* Do not divide Z, we use Z to back transform cut points in later chaining process. */ vt[i].fbcoord[0] /= vt[i].fbcoord[3]; vt[i].fbcoord[1] /= vt[i].fbcoord[3]; @@ -1483,7 +1483,7 @@ static void lineart_geometry_object_load(Depsgraph *dg, LineartTriangleAdjacent *orta; double new_mvp[4][4], new_mv[4][4], normal[4][4]; float imat[4][4]; - LineartElementLinkNode *reln; + LineartElementLinkNode *eln; LineartVert *orv; LineartEdge *o_la_e; LineartTriangle *ort; @@ -1583,10 +1583,10 @@ static void lineart_geometry_object_load(Depsgraph *dg, orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob; - reln = lineart_list_append_pointer_pool_sized( + eln = lineart_list_append_pointer_pool_sized( &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode)); - reln->element_count = bm->totvert; - reln->object_ref = orig_ob; + eln->element_count = bm->totvert; + eln->object_ref = orig_ob; if (ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { use_crease = cosf(M_PI - ob->lineart.crease_threshold); @@ -1598,14 +1598,14 @@ static void lineart_geometry_object_load(Depsgraph *dg, /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates * erroneous detection on creases. Future configuration should allow options. */ if (ob->type == OB_FONT) { - reln->flags |= LRT_ELEMENT_BORDER_ONLY; + eln->flags |= LRT_ELEMENT_BORDER_ONLY; } - reln = lineart_list_append_pointer_pool_sized( + eln = lineart_list_append_pointer_pool_sized( &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode)); - reln->element_count = bm->totface; - reln->object_ref = orig_ob; - reln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); + eln->element_count = bm->totface; + eln->object_ref = orig_ob; + eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); /* Note this memory is not from pool, will be deleted after culling. */ orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent"); @@ -1678,10 +1678,10 @@ static void lineart_geometry_object_load(Depsgraph *dg, } o_la_e = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); - reln = lineart_list_append_pointer_pool_sized( + eln = lineart_list_append_pointer_pool_sized( &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode)); - reln->element_count = allocate_la_e; - reln->object_ref = orig_ob; + eln->element_count = allocate_la_e; + eln->object_ref = orig_ob; la_e = o_la_e; for (i = 0; i < bm->totedge; i++) { @@ -3349,9 +3349,9 @@ static void lineart_main_add_triangles(LineartRenderBuffer *rb) int x1, x2, y1, y2; int r, co; - LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) { - tri = reln->pointer; - lim = reln->element_count; + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + tri = eln->pointer; + lim = eln->element_count; for (i = 0; i < lim; i++) { if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) { tri = (void *)(((uchar *)tri) + rb->triangle_size); -- cgit v1.2.3 From 6ad4b8b764a80b9deccd8e53b8c754829dda5e92 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Thu, 27 May 2021 20:33:02 +0800 Subject: LineArt: List Optimization for tile linked data. Use array instead of ListBase for line art bounding area linked triangles and edges. Reviewed By: Sebastian Parborg (zeddb) Differential Revision: https://developer.blender.org/D11302 --- .../gpencil_modifiers/intern/lineart/MOD_lineart.h | 26 ++++++- .../intern/lineart/lineart_chain.c | 4 +- .../gpencil_modifiers/intern/lineart/lineart_cpu.c | 80 ++++++++++++++++++---- 3 files changed, 93 insertions(+), 17 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 4e0585c9f6d..712e92d017d 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -205,6 +205,17 @@ typedef struct LineartChainRegisterEntry { char is_left; } LineartChainRegisterEntry; +enum eLineArtTileRecursiveLimit { + /* If tile gets this small, it's already much smaller than a pixel. No need to continue + * splitting. */ + LRT_TILE_RECURSIVE_PERSPECTIVE = 30, + /* This is a tried-and-true safe value for high poly models that also needed ortho rendering. */ + LRT_TILE_RECURSIVE_ORTHO = 10, +}; + +#define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100 +#define LRT_TILE_EDGE_COUNT_INITIAL 32 + typedef struct LineartRenderBuffer { struct LineartRenderBuffer *prev, *next; @@ -219,6 +230,11 @@ typedef struct LineartRenderBuffer { struct LineartBoundingArea *initial_bounding_areas; unsigned int bounding_area_count; + /* When splitting bounding areas, if there's an ortho camera placed at a straight angle, there + * will be a lot of triangles aligned in line which can not be separated by continue subdividing + * the tile. So we set a strict limit when using ortho camera. See eLineArtTileRecursiveLimit. */ + int tile_recursive_level; + ListBase vertex_buffer_pointers; ListBase line_buffer_pointers; ListBase triangle_buffer_pointers; @@ -363,10 +379,14 @@ typedef struct LineartBoundingArea { ListBase up; ListBase bp; - short triangle_count; + int16_t triangle_count; + int16_t max_triangle_count; + int16_t line_count; + int16_t max_line_count; - ListBase linked_triangles; - ListBase linked_edges; + /* Use array for speeding up multiple accesses. */ + struct LineartTriangle **linked_triangles; + struct LineartEdge **linked_lines; /** Reserved for image space reduction && multi-thread chaining. */ ListBase linked_chains; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index c8e4e93843a..23928b4ccda 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -40,8 +40,8 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, LineartVert **new_vt, int match_flag) { - LISTBASE_FOREACH (LinkData *, lip, &ba->linked_edges) { - LineartEdge *n_e = lip->data; + for (int i = 0; i < ba->line_count; i++) { + LineartEdge *n_e = ba->linked_lines[i]; if ((!(n_e->flags & LRT_EDGE_FLAG_ALL_TYPE)) || (n_e->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) { continue; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 24c405f1d67..0b439c20d65 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -314,6 +314,36 @@ BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, Linea (v2->base.flag && v2->intersecting_with == tri)); } +static void lineart_bounding_area_triangle_add(LineartRenderBuffer *rb, + LineartBoundingArea *ba, + LineartTriangle *tri) +{ + if (ba->triangle_count >= ba->max_triangle_count) { + LineartTriangle **new_array = lineart_mem_acquire( + &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count * 2); + memcpy(new_array, ba->linked_triangles, sizeof(LineartTriangle *) * ba->max_triangle_count); + ba->max_triangle_count *= 2; + ba->linked_triangles = new_array; + } + ba->linked_triangles[ba->triangle_count] = tri; + ba->triangle_count++; +} + +static void lineart_bounding_area_line_add(LineartRenderBuffer *rb, + LineartBoundingArea *ba, + LineartEdge *e) +{ + if (ba->line_count >= ba->max_line_count) { + LineartEdge **new_array = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartEdge *) * ba->max_line_count * 2); + memcpy(new_array, ba->linked_lines, sizeof(LineartEdge *) * ba->max_line_count); + ba->max_line_count *= 2; + ba->linked_lines = new_array; + } + ba->linked_lines[ba->line_count] = e; + ba->line_count++; +} + static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *e, int thread_id) { double x = e->v1->fbcoord[0], y = e->v1->fbcoord[1]; @@ -334,8 +364,8 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * while (nba) { - LISTBASE_FOREACH (LinkData *, lip, &nba->linked_triangles) { - tri = lip->data; + for (int i = 0; i < nba->triangle_count; i++) { + tri = (LineartTriangleThread *)nba->linked_triangles[i]; /* If we are already testing the line in this thread, then don't do it. */ if (tri->testing_e[thread_id] == e || (tri->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) || lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)tri)) { @@ -2499,6 +2529,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, BLI_addtail(&result->segments, es); /* Don't need to OR flags right now, just a type mark. */ result->flags = LRT_EDGE_FLAG_INTERSECTION; + lineart_prepend_edge_direct(&rb->intersection.first, result); int r1, r2, c1, c2, row, col; if (lineart_get_edge_bounding_areas(rb, result, &r1, &r2, &c1, &c2)) { @@ -2521,7 +2552,6 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, * See definition of LineartTriangleThread for more info. */ LineartTriangle *testing_triangle; LineartTriangleThread *tt; - LinkData *lip, *next_lip; double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc; @@ -2535,9 +2565,8 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, } /* If this _is_ the smallest subdiv bounding area, then do the intersections there. */ - for (lip = ba->linked_triangles.first; lip; lip = next_lip) { - next_lip = lip->next; - testing_triangle = lip->data; + for (int i = 0; i < ba->triangle_count; i++) { + testing_triangle = ba->linked_triangles[i]; tt = (LineartTriangleThread *)testing_triangle; if (testing_triangle == tri || tt->testing_e[0] == (LineartEdge *)tri) { @@ -2660,6 +2689,13 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->w = scene->r.xsch; rb->h = scene->r.ysch; + if (rb->cam_is_persp) { + rb->tile_recursive_level = LRT_TILE_RECURSIVE_PERSPECTIVE; + } + else { + rb->tile_recursive_level = LRT_TILE_RECURSIVE_ORTHO; + } + double asp = ((double)rb->w / (double)rb->h); rb->shift_x = (asp >= 1) ? c->shiftx : c->shiftx * asp; rb->shift_y = (asp <= 1) ? c->shifty : c->shifty * asp; @@ -2735,6 +2771,14 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) ba->cx = (ba->l + ba->r) / 2; ba->cy = (ba->u + ba->b) / 2; + /* Init linked_triangles array. */ + ba->max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT; + ba->max_line_count = LRT_TILE_EDGE_COUNT_INITIAL; + ba->linked_triangles = lineart_mem_acquire( + &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count); + ba->linked_lines = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartEdge *) * ba->max_line_count); + /* Link adjacent ones. */ if (row) { lineart_list_append_pointer_pool( @@ -2949,7 +2993,18 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb, lineart_bounding_areas_connect_new(rb, root); - while ((tri = lineart_list_pop_pointer_no_free(&root->linked_triangles)) != NULL) { + /* Init linked_triangles array. */ + for (int i = 0; i < 4; i++) { + ba[i].max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT; + ba[i].max_line_count = LRT_TILE_EDGE_COUNT_INITIAL; + ba[i].linked_triangles = lineart_mem_acquire( + &rb->render_data_pool, sizeof(LineartTriangle *) * LRT_TILE_SPLITTING_TRIANGLE_LIMIT); + ba[i].linked_lines = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartEdge *) * LRT_TILE_EDGE_COUNT_INITIAL); + } + + for (int i = 0; i < root->triangle_count; i++) { + tri = root->linked_triangles[i]; LineartBoundingArea *cba = root->child; double b[4]; b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); @@ -2970,7 +3025,8 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb, } } - while ((e = lineart_list_pop_pointer_no_free(&root->linked_edges)) != NULL) { + for (int i = 0; i < root->line_count; i++) { + e = root->linked_lines[i]; lineart_bounding_area_link_edge(rb, root, e); } @@ -3070,13 +3126,13 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, return; } if (root_ba->child == NULL) { - lineart_list_append_pointer_pool(&root_ba->linked_triangles, &rb->render_data_pool, tri); - root_ba->triangle_count++; + lineart_bounding_area_triangle_add(rb, root_ba, tri); /* If splitting doesn't improve triangle separation, then shouldn't allow splitting anymore. * Here we use recursive limit. This is especially useful in orthographic render, * where a lot of faces could easily line up perfectly in image space, * which can not be separated by simply slicing the image tile. */ - if (root_ba->triangle_count > 200 && recursive && recursive_level < 10) { + if (root_ba->triangle_count >= LRT_TILE_SPLITTING_TRIANGLE_LIMIT && recursive && + recursive_level < rb->tile_recursive_level) { lineart_bounding_area_split(rb, root_ba, recursive_level); } if (recursive && do_intersection && rb->use_intersections) { @@ -3118,7 +3174,7 @@ static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, LineartEdge *e) { if (root_ba->child == NULL) { - lineart_list_append_pointer_pool(&root_ba->linked_edges, &rb->render_data_pool, e); + lineart_bounding_area_line_add(rb, root_ba, e); } else { if (lineart_bounding_area_edge_intersect( -- cgit v1.2.3 From 9200e731360b199c638a42831e5d915d171b6515 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Thu, 27 May 2021 22:40:30 +1000 Subject: Cleanup: remove duplicate LIB_TAG_NEW untag code This patch removes unnecessary calls to `BKE_main_id_tag_all` where the same job is done by `BKE_main_id_clear_newpoins` on the following line. Reviewed By: campbellbarton, mont29 Ref D11379 --- source/blender/blenkernel/intern/collection.c | 2 -- source/blender/blenkernel/intern/object.c | 2 -- source/blender/blenkernel/intern/scene.c | 2 -- source/blender/editors/object/object_add.c | 2 -- 4 files changed, 8 deletions(-) diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 3170c3aa65c..3340888ee58 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -694,7 +694,6 @@ Collection *BKE_collection_duplicate(Main *bmain, const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; if (!is_subprocess) { - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate * all expected linked data. */ @@ -726,7 +725,6 @@ Collection *BKE_collection_duplicate(Main *bmain, #endif /* Cleanup. */ - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); BKE_main_collection_sync(bmain); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index e4cfe64df11..23813049053 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2633,7 +2633,6 @@ Object *BKE_object_duplicate(Main *bmain, const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; if (!is_subprocess) { - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate * all expected linked data. */ @@ -2773,7 +2772,6 @@ Object *BKE_object_duplicate(Main *bmain, #endif /* Cleanup. */ - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index a4ab64a8a02..b5e3432d44c 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1985,7 +1985,6 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) const bool is_subprocess = false; if (!is_subprocess) { - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); /* In case root duplicated ID is linked, assume we want to get a local copy of it and * duplicate all expected linked data. */ @@ -2027,7 +2026,6 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) #endif /* Cleanup. */ - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); BKE_main_collection_sync(bmain); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index b0255e79858..627787581d1 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2148,7 +2148,6 @@ static void copy_object_set_idnew(bContext *C) FOREACH_MAIN_ID_END; #endif - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); } @@ -3386,7 +3385,6 @@ static int duplicate_exec(bContext *C, wmOperator *op) /* We need to handle that here ourselves, because we may duplicate several objects, in which case * we also want to remap pointers between those... */ - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); CTX_DATA_BEGIN (C, Base *, base, selected_bases) { -- cgit v1.2.3 From 5721c89ba8a5d6b2a1cb275a49aa63ba25fc61cb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 27 May 2021 22:44:02 +1000 Subject: Cleanup: rename BKE_main_id_{clear_newpoins => newptr_and_tag_clear} It wasn't obvious this function cleared the tag as well. --- source/blender/blenkernel/BKE_lib_id.h | 2 +- source/blender/blenkernel/intern/collection.c | 4 ++-- source/blender/blenkernel/intern/lib_id.c | 4 ++-- source/blender/blenkernel/intern/lib_override.c | 4 ++-- source/blender/blenkernel/intern/object.c | 8 ++++---- source/blender/blenkernel/intern/scene.c | 4 ++-- source/blender/editors/interface/interface_templates.c | 4 ++-- source/blender/editors/object/object_add.c | 10 +++++----- source/blender/editors/object/object_relations.c | 4 ++-- source/blender/editors/space_outliner/outliner_tools.c | 4 ++-- source/blender/editors/space_outliner/outliner_tree.c | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 6b706f3bcd0..7ac45ac4883 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -273,7 +273,7 @@ void BKE_main_id_tag_all(struct Main *mainvar, const int tag, const bool value); void BKE_main_id_flag_listbase(struct ListBase *lb, const int flag, const bool value); void BKE_main_id_flag_all(struct Main *bmain, const int flag, const bool value); -void BKE_main_id_clear_newpoins(struct Main *bmain); +void BKE_main_id_newptr_and_tag_clear(struct Main *bmain); void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_only); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 3340888ee58..d8fbdf26d93 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -694,7 +694,7 @@ Collection *BKE_collection_duplicate(Main *bmain, const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; if (!is_subprocess) { - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate * all expected linked data. */ if (ID_IS_LINKED(collection)) { @@ -725,7 +725,7 @@ Collection *BKE_collection_duplicate(Main *bmain, #endif /* Cleanup. */ - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_collection_sync(bmain); } diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 8ad524dea21..f93bf494ee9 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1754,7 +1754,7 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) } /* next to indirect usage in read/writefile also in editobject.c scene.c */ -void BKE_main_id_clear_newpoins(Main *bmain) +void BKE_main_id_newptr_and_tag_clear(Main *bmain) { ID *id; @@ -2168,7 +2168,7 @@ void BKE_library_make_local(Main *bmain, TIMEIT_VALUE_PRINT(make_local); #endif - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BLI_memarena_free(linklist_mem); #ifdef DEBUG_TIME diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index aa7dfdf2bfa..0066aab03c2 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -845,7 +845,7 @@ bool BKE_lib_override_library_create( bmain, scene, view_layer, id_root, id_reference, NULL, false); /* Cleanup. */ - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */ @@ -1271,7 +1271,7 @@ bool BKE_lib_override_library_resync(Main *bmain, } /* Cleanup. */ - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* That one should not be needed in fact. */ return success; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 23813049053..b73f6a5b78c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2622,8 +2622,8 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src) * * \note This function does not do any remapping to new IDs, caller must do it * (\a #BKE_libblock_relink_to_newid()). - * \note Caller MUST free \a newid pointers itself (#BKE_main_id_clear_newpoins()) and call updates - * of DEG too (#DAG_relations_tag_update()). + * \note Caller MUST free \a newid pointers itself (#BKE_main_id_newptr_and_tag_clear()) and call + * updates of DEG too (#DAG_relations_tag_update()). */ Object *BKE_object_duplicate(Main *bmain, Object *ob, @@ -2633,7 +2633,7 @@ Object *BKE_object_duplicate(Main *bmain, const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; if (!is_subprocess) { - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate * all expected linked data. */ if (ID_IS_LINKED(ob)) { @@ -2772,7 +2772,7 @@ Object *BKE_object_duplicate(Main *bmain, #endif /* Cleanup. */ - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); } if (obn->type == OB_ARMATURE) { diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index b5e3432d44c..a58db89ad0c 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1985,7 +1985,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) const bool is_subprocess = false; if (!is_subprocess) { - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); /* In case root duplicated ID is linked, assume we want to get a local copy of it and * duplicate all expected linked data. */ if (ID_IS_LINKED(sce)) { @@ -2026,7 +2026,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) #endif /* Cleanup. */ - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_collection_sync(bmain); } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index dad3ccbe213..3990ad68c4d 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -653,7 +653,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) /* Only remap that specific ID usage to overriding local data-block. */ ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false); if (override_id != NULL) { - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); if (GS(override_id->name) == ID_OB) { Scene *scene = CTX_data_scene(C); @@ -672,7 +672,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)) { - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); /* reassign to get get proper updates/notifiers */ idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 627787581d1..ef500be0133 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2148,7 +2148,7 @@ static void copy_object_set_idnew(bContext *C) FOREACH_MAIN_ID_END; #endif - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); } /** \} */ @@ -2469,7 +2469,7 @@ static void make_object_duplilist_real(bContext *C, free_object_duplilist(lb_duplis); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); base->object->transflag &= ~OB_DUPLI; DEG_id_tag_update(&base->object->id, ID_RECALC_COPY_ON_WRITE); @@ -2484,7 +2484,7 @@ static int object_duplicates_make_real_exec(bContext *C, wmOperator *op) const bool use_base_parent = RNA_boolean_get(op->ptr, "use_base_parent"); const bool use_hierarchy = RNA_boolean_get(op->ptr, "use_hierarchy"); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) { make_object_duplilist_real(C, depsgraph, scene, base, use_base_parent, use_hierarchy); @@ -3369,7 +3369,7 @@ Base *ED_object_add_duplicate( DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS); } - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); return basen; } @@ -3385,7 +3385,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) /* We need to handle that here ourselves, because we may duplicate several objects, in which case * we also want to remap pointers between those... */ - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); CTX_DATA_BEGIN (C, Base *, base, selected_bases) { Base *basen = object_add_duplicate_internal( diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index b685a93f27b..127d8681d3c 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1944,7 +1944,7 @@ void ED_object_single_user(Main *bmain, Scene *scene, Object *ob) ob->flag |= OB_DONE; single_object_users(bmain, scene, NULL, OB_DONE, false); - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); } static void single_obdata_users( @@ -2644,7 +2644,7 @@ static int make_single_user_exec(bContext *C, wmOperator *op) single_object_action_users(bmain, scene, view_layer, v3d, flag); } - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); WM_event_add_notifier(C, NC_WINDOW, NULL); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index c532dd8accd..da47fd29549 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -740,7 +740,7 @@ static void id_local_fn(bContext *C, BKE_lib_id_clear_library_data(bmain, tselem->id); } else { - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); } } else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { @@ -852,7 +852,7 @@ static void id_override_library_create_fn(bContext *C, success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != NULL; /* Cleanup. */ - BKE_main_id_clear_newpoins(bmain); + BKE_main_id_newptr_and_tag_clear(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index bed7683703f..90389fc1be2 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1925,5 +1925,5 @@ void outliner_build_tree(Main *mainvar, outliner_filter_tree(space_outliner, view_layer); outliner_restore_scrolling_position(space_outliner, region, &focus); - BKE_main_id_clear_newpoins(mainvar); + BKE_main_id_newptr_and_tag_clear(mainvar); } -- cgit v1.2.3 From a12fd5c3ada56c52e5cf1ee98877c79433f1f1c1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 27 May 2021 09:27:08 -0400 Subject: Cleanup: Use consistent variable names --- source/blender/blenkernel/BKE_spline.hh | 8 ++++---- source/blender/blenkernel/intern/spline_bezier.cc | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 9e5552082af..8d679a445b7 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -257,10 +257,10 @@ class BezierSpline final : public Spline { void set_resolution(const int value); void add_point(const blender::float3 position, - const HandleType handle_type_start, - const blender::float3 handle_position_start, - const HandleType handle_type_end, - const blender::float3 handle_position_end, + const HandleType handle_type_left, + const blender::float3 handle_position_left, + const HandleType handle_type_right, + const blender::float3 handle_position_right, const float radius, const float tilt); diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 4be3ba8576e..4f2625c14d3 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -59,18 +59,18 @@ void BezierSpline::set_resolution(const int value) * \warning Call #reallocate on the spline's attributes after adding all points. */ void BezierSpline::add_point(const float3 position, - const HandleType handle_type_start, - const float3 handle_position_start, - const HandleType handle_type_end, - const float3 handle_position_end, + const HandleType handle_type_left, + const float3 handle_position_left, + const HandleType handle_type_right, + const float3 handle_position_right, const float radius, const float tilt) { - handle_types_left_.append(handle_type_start); - handle_positions_left_.append(handle_position_start); + handle_types_left_.append(handle_type_left); + handle_positions_left_.append(handle_position_left); positions_.append(position); - handle_types_right_.append(handle_type_end); - handle_positions_right_.append(handle_position_end); + handle_types_right_.append(handle_type_right); + handle_positions_right_.append(handle_position_right); radii_.append(radius); tilts_.append(tilt); this->mark_cache_invalid(); -- cgit v1.2.3 From 5621a8ed7fbcd9067c576d4066e83d9640a80c49 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 27 May 2021 09:37:19 -0400 Subject: Fix T88452: Point Separate crash on curve component The point separate node should create a point cloud from control points in this case, but for now disable the node on curves to avoid the crash. --- source/blender/nodes/geometry/nodes/node_geo_point_separate.cc | 4 ++++ 1 file changed, 4 insertions(+) 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 ff7e95e0221..312ca5b8c33 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -127,6 +127,10 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in, { GeometrySet set_out; for (const GeometryComponent *component : set_in.get_components_for_read()) { + if (component->type() == GEO_COMPONENT_TYPE_CURVE) { + /* Don't support the curve component for now, even though it has a point domain. */ + continue; + } GeometryComponent &out_component = set_out.get_component_for_write(component->type()); separate_points_from_component(*component, out_component, mask_name, invert); } -- cgit v1.2.3 From ac833108dbbaa5f2a6d62effe962eb289cc92291 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 27 May 2021 10:08:40 -0400 Subject: Geometry Nodes: Draw curve data in the viewport This patch adds relatively small changes to the curve draw cache implementation in order to draw the curve data in the viewport. The dependency graph iterator is also modified so that it iterates over the curve geometry component, which is presented to users as `Curve` data with a pointer to the `CurveEval` The idea with the spline data type in geometry nodes is that curve data itself is only the control points, and any evaluated data with faces is a mesh. That is mostly expected elsewhere in Blender anyway. This means it's only necessary to implement wire edge drawing of `CurveEval` data. Adding a `CurveEval` pointer to `Curve` is in line with changes I'd like to make in the future like using `CurveEval` in more places such as edit mode. An alternate solution involves converting the curve wire data to a mesh, however, that requires copying all of the data, and since avoiding it is rather simple and is in-line with future plans anyway, I think doing it this way is better. Differential Revision: https://developer.blender.org/D11351 --- source/blender/blenkernel/BKE_geometry_set.hh | 12 ++ .../blenkernel/intern/geometry_component_curve.cc | 33 +++++ .../depsgraph/intern/depsgraph_query_iter.cc | 23 +++ source/blender/draw/CMakeLists.txt | 1 + .../blender/draw/intern/draw_cache_impl_curve.cc | 161 +++++++++++++++------ source/blender/makesdna/DNA_curve_types.h | 7 + 6 files changed, 195 insertions(+), 42 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 0ed6eea7954..9b50a9042eb 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -39,6 +39,7 @@ struct Mesh; struct Object; struct PointCloud; struct Volume; +struct Curve; class CurveEval; enum class GeometryOwnershipType { @@ -406,6 +407,15 @@ class CurveComponent : public GeometryComponent { CurveEval *curve_ = nullptr; GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; + /** + * Curve data necessary to hold the draw cache for rendering, consistent over multiple redraws. + * This is necessary because Blender assumes that objects evaluate to an object data type, and + * we use #CurveEval rather than #Curve here. It also allows us to mostly reuse the same + * batch cache implementation. + */ + mutable Curve *curve_for_render_ = nullptr; + mutable std::mutex curve_for_render_mutex_; + public: CurveComponent(); ~CurveComponent(); @@ -430,6 +440,8 @@ class CurveComponent : public GeometryComponent { bool owns_direct_data() const override; void ensure_owns_direct_data() override; + const Curve *get_curve_for_render() const; + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; private: diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index be3fdf26cb3..de8dc355557 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -14,9 +14,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "DNA_ID_enums.h" +#include "DNA_curve_types.h" + #include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" +#include "BKE_curve.h" #include "BKE_geometry_set.hh" +#include "BKE_lib_id.h" #include "BKE_spline.hh" #include "attribute_access_intern.hh" @@ -58,6 +63,11 @@ void CurveComponent::clear() if (ownership_ == GeometryOwnershipType::Owned) { delete curve_; } + if (curve_for_render_ != nullptr) { + BKE_id_free(nullptr, curve_for_render_); + curve_for_render_ = nullptr; + } + curve_ = nullptr; } } @@ -118,6 +128,29 @@ void CurveComponent::ensure_owns_direct_data() } } +/** + * Create empty curve data used for rendering the spline's wire edges. + * \note See comment on #curve_for_render_ for further explanation. + */ +const Curve *CurveComponent::get_curve_for_render() const +{ + if (curve_ == nullptr) { + return nullptr; + } + if (curve_for_render_ != nullptr) { + return curve_for_render_; + } + std::lock_guard lock{curve_for_render_mutex_}; + if (curve_for_render_ != nullptr) { + return curve_for_render_; + } + + curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU, nullptr); + curve_for_render_->curve_eval = curve_; + + return curve_for_render_; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index ed002321729..df1cf8cc771 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -219,6 +219,29 @@ bool deg_iterator_components_step(BLI_Iterator *iter) } } + /* 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; } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 045adf4b380..afb0f613290 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC ../depsgraph ../editors/include ../editors/space_view3d + ../functions ../gpu ../imbuf ../makesdna diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index ddafc7205bb..5cf99db5485 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -25,8 +25,11 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.hh" +#include "BLI_float3.hh" #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_span.hh" #include "BLI_utildefines.h" #include "DNA_curve_types.h" @@ -34,6 +37,8 @@ #include "BKE_curve.h" #include "BKE_displist.h" #include "BKE_font.h" +#include "BKE_geometry_set.hh" +#include "BKE_spline.hh" #include "GPU_batch.h" #include "GPU_capabilities.h" @@ -48,6 +53,11 @@ #include "draw_cache_impl.h" /* own include */ +using blender::Array; +using blender::float3; +using blender::IndexRange; +using blender::Span; + /* See: edit_curve_point_vert.glsl for duplicate includes. */ #define SELECT 1 #define ACTIVE_NURB (1 << 2) @@ -139,6 +149,21 @@ static void curve_render_wire_verts_edges_len_get(const CurveCache *ob_curve_cac } } +static void curve_eval_render_wire_verts_edges_len_get(const CurveEval &curve_eval, + int *r_curve_len, + int *r_vert_len, + int *r_edge_len) +{ + Span splines = curve_eval.splines(); + *r_curve_len = splines.size(); + *r_vert_len = 0; + *r_edge_len = 0; + for (const SplinePtr &spline : splines) { + *r_vert_len += spline->evaluated_points_size(); + *r_edge_len += spline->evaluated_edges_size(); + } +} + static int curve_render_normal_len_get(const ListBase *lb, const CurveCache *ob_curve_cache) { int normal_len = 0; @@ -192,6 +217,9 @@ struct CurveRenderData { /* borrow from 'Object' */ CurveCache *ob_curve_cache; + /* Owned by the evaluated object's geometry set (#geometry_set_eval). */ + const CurveEval *curve_eval; + /* borrow from 'Curve' */ ListBase *nurbs; @@ -230,11 +258,21 @@ static CurveRenderData *curve_render_data_create(Curve *cu, rdata->ob_curve_cache = ob_curve_cache; + rdata->curve_eval = cu->curve_eval; + if (types & CU_DATATYPE_WIRE) { - curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache, - &rdata->wire.curve_len, - &rdata->wire.vert_len, - &rdata->wire.edge_len); + if (rdata->curve_eval != nullptr) { + curve_eval_render_wire_verts_edges_len_get(*rdata->curve_eval, + &rdata->wire.curve_len, + &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) { @@ -556,8 +594,6 @@ void DRW_curve_batch_cache_free(Curve *cu) /* GPUBatch cache usage. */ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos) { - BLI_assert(rdata->ob_curve_cache != nullptr); - static GPUVertFormat format = {0}; static struct { uint pos; @@ -570,30 +606,46 @@ 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); - 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); + 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]); + } } } - 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]); + 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]); + } } } + BLI_assert(v_idx == vert_len); } - BLI_assert(v_idx == vert_len); } static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines) { - BLI_assert(rdata->ob_curve_cache != nullptr); - 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); @@ -603,34 +655,56 @@ 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); - 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); + 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()) { + 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); } - GPU_indexbuf_add_primitive_restart(&elb); - v_idx += bl->nr; } - 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; + 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 bool is_cyclic = bl->poly != -1; if (is_cyclic) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1)); + GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1)); } - for (int i = 0; i < dl->nr; i++) { + 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 += dl->nr; + v_idx += bl->nr; + } + 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; + } } } + GPU_indexbuf_build_in_place(&elb, ibo_curve_lines); } @@ -1070,8 +1144,11 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen CurveRenderData *rdata = curve_render_data_create(cu, ob->runtime.curve_cache, mr_flag); - /* DispLists */ - ListBase *lb = &rdata->ob_curve_cache->disp; + /* The object's curve cache can be empty (in one case because we use #CurveEval's cache instead), + * If so, point to an empty DispList list to avoid the need to check for null in the following + * functions. */ + ListBase empty_lb = {nullptr, nullptr}; + ListBase *lb = rdata->ob_curve_cache == nullptr ? &empty_lb : &rdata->ob_curve_cache->disp; /* Generate VBOs */ if (DRW_vbo_requested(cache->ordered.pos_nor)) { diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 716c480bab8..f6242679808 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -43,6 +43,7 @@ struct Key; struct Material; struct Object; struct VFont; +struct CurveEval; /* These two Lines with # tell makesdna this struct can be excluded. */ # @@ -300,6 +301,12 @@ typedef struct Curve { char _pad2[6]; 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. + */ + struct CurveEval *curve_eval; + void *batch_cache; } Curve; -- cgit v1.2.3 From e8ca635e43f0b642361c898797c3a3ab3f7dc27e Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Thu, 27 May 2021 08:10:31 -0600 Subject: Cleanup: rename blender-launcher source file. blender-laucher.c was not an ideal name for this file since it's not directly clear it is windows only. This change renames it to blender_launcher_win32.c to be more in line with other win32 specific files we have. --- source/creator/CMakeLists.txt | 2 +- source/creator/blender-launcher.c | 92 --------------------------------- source/creator/blender_launcher_win32.c | 92 +++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 93 deletions(-) delete mode 100644 source/creator/blender-launcher.c create mode 100644 source/creator/blender_launcher_win32.c diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index baea3ad1f9b..6a768106d9e 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -287,7 +287,7 @@ else() add_executable(blender ${EXETYPE} ${SRC}) if(WIN32) add_executable(blender-launcher WIN32 - blender-launcher.c + blender_launcher_win32.c ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.rc ${CMAKE_BINARY_DIR}/blender.exe.manifest ) diff --git a/source/creator/blender-launcher.c b/source/creator/blender-launcher.c deleted file mode 100644 index 86b0f4f3b97..00000000000 --- a/source/creator/blender-launcher.c +++ /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 -#include - -#include - -int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) -{ - STARTUPINFO siStartInfo = {0}; - PROCESS_INFORMATION procInfo; - wchar_t path[MAX_PATH]; - - siStartInfo.wShowWindow = SW_HIDE; - siStartInfo.dwFlags = STARTF_USESHOWWINDOW; - - /* Get the path to the currently running executable (blender-launcher.exe) */ - - DWORD nSize = GetModuleFileName(NULL, path, MAX_PATH); - if (!nSize) { - return -1; - } - - /* GetModuleFileName returns the number of characters written, but GetLastError needs to be - * called to see if it ran out of space or not. However where would we be without exceptions - * to the rule: "If the buffer is too small to hold the module name, the function returns nSize. - * The last error code remains ERROR_SUCCESS." - source: MSDN. */ - - if (GetLastError() == ERROR_SUCCESS && nSize == MAX_PATH) { - return -1; - } - - /* Remove the filename (blender-launcher.exe) from path. */ - if (PathCchRemoveFileSpec(path, MAX_PATH) != S_OK) { - return -1; - } - - /* Add blender.exe to path, resulting in the full path to the blender executable. */ - if (PathCchCombine(path, MAX_PATH, path, L"blender.exe") != S_OK) { - return -1; - } - - int required_size_chars = lstrlenW(path) + /* Module name */ - 3 + /* 2 quotes + Space */ - lstrlenW(pCmdLine) + /* Original command line */ - 1; /* Zero terminator */ - size_t required_size_bytes = required_size_chars * sizeof(wchar_t); - wchar_t *buffer = (wchar_t *)malloc(required_size_bytes); - if (!buffer) { - return -1; - } - - if (StringCbPrintfEx(buffer, - required_size_bytes, - NULL, - NULL, - STRSAFE_NULL_ON_FAILURE, - L"\"%s\" %s", - path, - pCmdLine) != S_OK) { - free(buffer); - return -1; - } - - BOOL success = CreateProcess( - path, buffer, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &procInfo); - - if (success) { - /* Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer - * needed - MSDN. Closing the handles will NOT terminate the thread/process that we just - * started. */ - CloseHandle(procInfo.hThread); - CloseHandle(procInfo.hProcess); - } - - free(buffer); - return success ? 0 : -1; -} diff --git a/source/creator/blender_launcher_win32.c b/source/creator/blender_launcher_win32.c new file mode 100644 index 00000000000..86b0f4f3b97 --- /dev/null +++ b/source/creator/blender_launcher_win32.c @@ -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 +#include + +#include + +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) +{ + STARTUPINFO siStartInfo = {0}; + PROCESS_INFORMATION procInfo; + wchar_t path[MAX_PATH]; + + siStartInfo.wShowWindow = SW_HIDE; + siStartInfo.dwFlags = STARTF_USESHOWWINDOW; + + /* Get the path to the currently running executable (blender-launcher.exe) */ + + DWORD nSize = GetModuleFileName(NULL, path, MAX_PATH); + if (!nSize) { + return -1; + } + + /* GetModuleFileName returns the number of characters written, but GetLastError needs to be + * called to see if it ran out of space or not. However where would we be without exceptions + * to the rule: "If the buffer is too small to hold the module name, the function returns nSize. + * The last error code remains ERROR_SUCCESS." - source: MSDN. */ + + if (GetLastError() == ERROR_SUCCESS && nSize == MAX_PATH) { + return -1; + } + + /* Remove the filename (blender-launcher.exe) from path. */ + if (PathCchRemoveFileSpec(path, MAX_PATH) != S_OK) { + return -1; + } + + /* Add blender.exe to path, resulting in the full path to the blender executable. */ + if (PathCchCombine(path, MAX_PATH, path, L"blender.exe") != S_OK) { + return -1; + } + + int required_size_chars = lstrlenW(path) + /* Module name */ + 3 + /* 2 quotes + Space */ + lstrlenW(pCmdLine) + /* Original command line */ + 1; /* Zero terminator */ + size_t required_size_bytes = required_size_chars * sizeof(wchar_t); + wchar_t *buffer = (wchar_t *)malloc(required_size_bytes); + if (!buffer) { + return -1; + } + + if (StringCbPrintfEx(buffer, + required_size_bytes, + NULL, + NULL, + STRSAFE_NULL_ON_FAILURE, + L"\"%s\" %s", + path, + pCmdLine) != S_OK) { + free(buffer); + return -1; + } + + BOOL success = CreateProcess( + path, buffer, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &procInfo); + + if (success) { + /* Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer + * needed - MSDN. Closing the handles will NOT terminate the thread/process that we just + * started. */ + CloseHandle(procInfo.hThread); + CloseHandle(procInfo.hProcess); + } + + free(buffer); + return success ? 0 : -1; +} -- cgit v1.2.3 From a3edf4a3814dfc1d18282298c49979d19ea2bcd0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 27 May 2021 10:50:43 -0400 Subject: Fix build error: Make CurveEval a struct We need a pointer to this in DNA, which means it cannot be a class. --- source/blender/blenkernel/BKE_spline.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 8d679a445b7..ef76c699cbb 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -483,7 +483,7 @@ class PolySpline final : public Spline { * A #CurveEval corresponds to the #Curve object data. The name is different for clarity, since * more of the data is stored in the splines, but also just to be different than the name in DNA. */ -class CurveEval { +struct CurveEval { private: blender::Vector splines_; -- cgit v1.2.3 From 24b2482ad9d2308e4a0b784f980ad1e1b18f1f8c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 27 May 2021 11:00:00 -0400 Subject: Cleanup: Fix forward declaring class with "struct" --- source/blender/blenkernel/BKE_geometry_set.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 9b50a9042eb..b2342a5fd96 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -40,7 +40,7 @@ struct Object; struct PointCloud; struct Volume; struct Curve; -class CurveEval; +struct CurveEval; enum class GeometryOwnershipType { /* The geometry is owned. This implies that it can be changed. */ -- cgit v1.2.3 From 3025c348253a66f9c316259aa03f8884bf56d779 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 27 May 2021 11:06:08 -0400 Subject: Geometry Nodes: Expose texture and material inputs to modifier This allows choosing material and texture sockets for the group input node in the modifier. Note that currently grease pencil materials are displayed in the list, even though grease pencil data is not supported yet by geometry nodes. That is more complicated to fix in this case, since we use IDProperties to store the dynamic exposed inputs. Differential Revision: https://developer.blender.org/D11393 --- source/blender/modifiers/intern/MOD_nodes.cc | 89 +++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 29c04d6c571..8fa80025790 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -36,6 +36,7 @@ #include "DNA_collection_types.h" #include "DNA_defaults.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" @@ -123,6 +124,18 @@ static void addIdsUsedBySocket(const ListBase *sockets, Set &ids) ids.add(&collection->id); } } + else if (socket->type == SOCK_MATERIAL) { + Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value; + if (material != nullptr) { + ids.add(&material->id); + } + } + else if (socket->type == SOCK_TEXTURE) { + Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value; + if (texture != nullptr) { + ids.add(&texture->id); + } + } } } @@ -197,13 +210,25 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte find_used_ids_from_settings(nmd->settings, used_ids); find_used_ids_from_nodes(*nmd->node_group, used_ids); for (ID *id : used_ids) { - if (GS(id->name) == ID_OB) { - Object *object = reinterpret_cast(id); - add_object_relation(ctx, *object); - } - if (GS(id->name) == ID_GR) { - Collection *collection = reinterpret_cast(id); - add_collection_relation(ctx, *collection); + switch ((ID_Type)GS(id->name)) { + case ID_OB: { + Object *object = reinterpret_cast(id); + add_object_relation(ctx, *object); + break; + } + case ID_GR: { + Collection *collection = reinterpret_cast(id); + add_collection_relation(ctx, *collection); + break; + } + case ID_TE: { + DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier"); + } + default: { + /* Purposefully don't add relations for materials. While there are material sockets, + * the pointers are only passed around as handles rather than dereferenced. */ + break; + } } } } @@ -558,6 +583,48 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso }; return &collection_type; } + case SOCK_TEXTURE: { + static const SocketPropertyType collection_type = { + [](const bNodeSocket &socket, const char *name) { + bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, name); + }, + nullptr, + nullptr, + nullptr, + nullptr, + [](const IDProperty &property) { return property.type == IDP_ID; }, + [](const IDProperty &property, void *r_value) { + ID *id = IDP_Id(&property); + Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr; + *(Tex **)r_value = texture; + }, + }; + return &collection_type; + } + case SOCK_MATERIAL: { + static const SocketPropertyType collection_type = { + [](const bNodeSocket &socket, const char *name) { + bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, name); + }, + nullptr, + nullptr, + nullptr, + nullptr, + [](const IDProperty &property) { return property.type == IDP_ID; }, + [](const IDProperty &property, void *r_value) { + ID *id = IDP_Id(&property); + Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr; + *(Material **)r_value = material; + }, + }; + return &collection_type; + } default: { return nullptr; } @@ -1085,6 +1152,14 @@ static void draw_property_for_socket(uiLayout *layout, ICON_OUTLINER_COLLECTION); break; } + case SOCK_MATERIAL: { + uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "materials", socket.name, ICON_MATERIAL); + break; + } + case SOCK_TEXTURE: { + uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE); + break; + } default: uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); } -- cgit v1.2.3 From ba4228bcf77e50368a7c84affd5e380bd45789a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 27 May 2021 17:10:17 +0200 Subject: Revert "EEVEE: Ensure Reflection: Use new implementation" Both before and after can have artifacts with some normal maps, but this seems to give worse artifacts on average which are not worth the minor performance increase. This reverts commit 5c4d24e1fd752a8a89d44d05e8e3f9b31f2d7db0. Ref T88368, D10084 --- .../engines/eevee/shaders/bsdf_common_lib.glsl | 65 +++++++++++++++------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index fdbb70e917d..c8eaa06094e 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -138,30 +138,55 @@ void accumulate_light(vec3 light, float fac, inout vec4 accum) /* Same thing as Cycles without the comments to make it shorter. */ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) { - vec3 R; - float NI = dot(N, I); - float NgR, threshold; - /* Check if the incident ray is coming from behind normal N. */ - if (NI > 0.0) { - /* Normal reflection. */ - R = (2.0 * NI) * N - I; - NgR = dot(Ng, R); - /* Reflection rays may always be at least as shallow as the incoming ray. */ - threshold = min(0.9 * dot(Ng, I), 0.01); - if (NgR >= threshold) { - return N; + vec3 R = -reflect(I, N); + + /* Reflection rays may always be at least as shallow as the incoming ray. */ + float threshold = min(0.9 * dot(Ng, I), 0.025); + if (dot(Ng, R) >= threshold) { + return N; + } + + float NdotNg = dot(N, Ng); + vec3 X = normalize(N - NdotNg * Ng); + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = sqrt(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + float fac = 0.5 / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + bool valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5)); + bool valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5)); + + vec2 N_new; + if (valid1 && valid2) { + /* If both are possible, do the expensive reflection-based check. */ + vec2 N1 = vec2(sqrt(1.0 - N1_z2), sqrt(N1_z2)); + vec2 N2 = vec2(sqrt(1.0 - N2_z2), sqrt(N2_z2)); + + float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; + float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; + + valid1 = (R1 >= 1e-5); + valid2 = (R2 >= 1e-5); + if (valid1 && valid2) { + N_new = (R1 < R2) ? N1 : N2; } + else { + N_new = (R1 > R2) ? N1 : N2; + } + } + else if (valid1 || valid2) { + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new = vec2(sqrt(1.0 - Nz2), sqrt(Nz2)); } else { - /* Bad incident. */ - R = -I; - NgR = dot(Ng, R); - threshold = 0.01; + return Ng; } - /* Lift the reflection above the threshold. */ - R = R + Ng * (threshold - NgR); - /* Find a bisector. */ - return safe_normalize(I * length(R) + R * length(I)); + return N_new.x * X + N_new.y * Ng; } /* ----------- Cone angle Approximation --------- */ -- cgit v1.2.3 From dfa3dcbab9524e9f6da9be17eec4c74708a4889c Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 27 May 2021 15:30:02 +0200 Subject: Fix T88625: Multiobject UV hiding/unhiding does not work with UV_SYNC_SELECTION Oversight in {rB470f17f21c06}. Hiding was only done for the first mesh, then the operator finished (in case of UV_SYNC_SELECTION). Now just continue to the next. Maniphest Tasks: T88625 Differential Revision: https://developer.blender.org/D11413 --- source/blender/editors/uvedit/uvedit_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index e11341429a6..708f04bf044 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -1471,7 +1471,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) if (EDBM_mesh_hide(em, swap)) { EDBM_update_generic(ob->data, true, false); } - return OPERATOR_FINISHED; + continue; } BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { @@ -1609,7 +1609,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *op) if (EDBM_mesh_reveal(em, select)) { EDBM_update_generic(ob->data, true, false); } - return OPERATOR_FINISHED; + continue; } if (use_face_center) { if (em->selectmode == SCE_SELECT_FACE) { -- cgit v1.2.3 From 530f2994e9aeed551ce0cfec7a97a15234ebcb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Thu, 27 May 2021 19:05:17 +0200 Subject: Fix T88614: Mixdown crashes Blender 2.92.0 and 3.0.0 Alpha The problem is caused by the most recent ffmpeg version (4.4) which needs channels to be set when submitting a frame for encoding. --- extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp index 2cd3261bd20..10517d1d596 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp @@ -75,6 +75,7 @@ void FFMPEGWriter::encode() m_frame->nb_samples = m_input_samples; m_frame->format = m_codecCtx->sample_fmt; m_frame->channel_layout = m_codecCtx->channel_layout; + m_frame->channels = m_specs.channels; if(avcodec_fill_audio_frame(m_frame, m_specs.channels, m_codecCtx->sample_fmt, reinterpret_cast(data), m_input_buffer.getSize(), 0) < 0) AUD_THROW(FileException, "File couldn't be written, filling the audio frame failed with ffmpeg."); -- cgit v1.2.3 From 311ffd967bd15a0fd265d1831d3577982b01f55d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 27 May 2021 15:54:26 +0200 Subject: LibOverride: refactor recursive resync. We need to re-evaluate what needs to be resynced after each step of processing overrides from a given 'indirect level' of libraries. Otherwise, recusrive overrides (overrides of linked overrides) won't work. Note that this should not change too much in practice currently, since there are other issues with recursive overrides yet. Also, checks (CLOG errors) added show that some ID (node trees) seem to be detected as needing resynced even after beig just resynced, this needs further investigation still. Could be though that it is due to limit currently set on nodetrees, those are always complicated snowflakes to deal with... --- source/blender/blenkernel/intern/lib_override.c | 235 ++++++++++++++---------- 1 file changed, 136 insertions(+), 99 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 0066aab03c2..aeef6c04944 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1277,100 +1277,19 @@ bool BKE_lib_override_library_resync(Main *bmain, return success; } -static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) -{ - if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) { - return IDWALK_RET_NOP; - } - ID *id_owner = cb_data->id_owner; - ID *id = *cb_data->id_pointer; - if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) { - const int owner_library_indirect_level = id_owner->lib != NULL ? id_owner->lib->temp_index : 0; - if (owner_library_indirect_level > 10000) { - CLOG_ERROR( - &LOG, - "Levels of indirect usages of libraries is way too high, skipping further building " - "loops (Involves at least '%s' and '%s')", - id_owner->lib->filepath, - id->lib->filepath); - BLI_assert(0); - return IDWALK_RET_NOP; - } - - if (owner_library_indirect_level >= id->lib->temp_index) { - id->lib->temp_index = owner_library_indirect_level + 1; - *(bool *)cb_data->user_data = true; - } - } - return IDWALK_RET_NOP; -} - -/** Define the `temp_index` of libraries from their highest level of indirect usage. +/* Ensure resync of all overrides at one level of indirect usage. * - * E.g. if lib_a uses lib_b, lib_c and lib_d, and lib_b also uses lib_d, then lib_a has an index of - * 1, lib_b and lib_c an index of 2, and lib_d an index of 3. */ -static int lib_override_libraries_index_define(Main *bmain) -{ - LISTBASE_FOREACH (Library *, library, &bmain->libraries) { - /* index 0 is reserved for local data. */ - library->temp_index = 1; - } - bool do_continue = true; - while (do_continue) { - do_continue = false; - ID *id; - FOREACH_MAIN_ID_BEGIN (bmain, id) { - BKE_library_foreach_ID_link( - bmain, id, lib_override_sort_libraries_func, &do_continue, IDWALK_READONLY); - } - FOREACH_MAIN_ID_END; - } - - int library_indirect_level_max = 0; - LISTBASE_FOREACH (Library *, library, &bmain->libraries) { - if (library->temp_index > library_indirect_level_max) { - library_indirect_level_max = library->temp_index; - } - } - return library_indirect_level_max; -} - -/** - * Detect and handle required resync of overrides data, when relations between reference linked IDs - * have changed. - * - * This is a fairly complex and costly operation, typically it should be called after - * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases. - * - * This function will first detect the remaining cases requiring a resync (namely, either when an - * existing linked ID that did not require to be overridden before now would be, or when new IDs - * are added to the hierarchy). - * - * Then it will handle the resync of necessary IDs (through calls to - * #BKE_lib_override_library_resync). + * We need to handle each level independently, since an override at level n may be affected by + * other overrides from level n + 1 etc. (i.e. from linked overrides it may use). */ -void BKE_lib_override_library_main_resync(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - ReportList *reports) +static void lib_override_library_main_resync_on_library_indirect_level( + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + Collection *override_resync_residual_storage, + const int library_indirect_level, + ReportList *reports) { - /* We use a specific collection to gather/store all 'orphaned' override collections and objects - * generated by re-sync-process. This avoids putting them in scene's master collection. */ -#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS" - Collection *override_resync_residual_storage = BLI_findstring( - &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2); - if (override_resync_residual_storage != NULL && - override_resync_residual_storage->id.lib != NULL) { - override_resync_residual_storage = NULL; - } - if (override_resync_residual_storage == NULL) { - override_resync_residual_storage = BKE_collection_add( - bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME); - /* Hide the collection from viewport and render. */ - override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; - } - BKE_main_relations_create(bmain, 0); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); @@ -1410,6 +1329,16 @@ void BKE_lib_override_library_main_resync(Main *bmain, if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) { CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib); + if (id->lib->temp_index > library_indirect_level) { + CLOG_ERROR( + &LOG, + "While processing indirect level %d, ID %s from lib %s of indirect level %d detected " + "as needing resync.", + library_indirect_level, + id->name, + id->lib ? id->lib->filepath : "", + id->lib ? id->lib->temp_index : 0); + } continue; } @@ -1434,6 +1363,16 @@ void BKE_lib_override_library_main_resync(Main *bmain, id->lib, id_to->name, id_to->lib); + if (id->lib->temp_index > library_indirect_level) { + CLOG_ERROR(&LOG, + "While processing indirect level %d, ID %s from lib %s of indirect level %d " + "detected " + "as needing resync.", + library_indirect_level, + id->name, + id->lib ? id->lib->filepath : "", + id->lib ? id->lib->temp_index : 0); + } break; } } @@ -1443,15 +1382,13 @@ void BKE_lib_override_library_main_resync(Main *bmain, BKE_main_relations_free(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); - int library_indirect_level = lib_override_libraries_index_define(bmain); - /* And do the actual resync for all IDs detected as needing it. * NOTE: Since this changes `bmain` (adding **and** removing IDs), we cannot use * `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */ bool do_continue = true; while (do_continue) { - ListBase *lb; do_continue = false; + ListBase *lb; FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 || @@ -1481,14 +1418,114 @@ void BKE_lib_override_library_main_resync(Main *bmain, } } FOREACH_MAIN_LISTBASE_END; + } +} + +static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) +{ + if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) { + return IDWALK_RET_NOP; + } + ID *id_owner = cb_data->id_owner; + ID *id = *cb_data->id_pointer; + if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) { + const int owner_library_indirect_level = id_owner->lib != NULL ? id_owner->lib->temp_index : 0; + if (owner_library_indirect_level > 10000) { + CLOG_ERROR( + &LOG, + "Levels of indirect usages of libraries is way too high, skipping further building " + "loops (Involves at least '%s' and '%s')", + id_owner->lib->filepath, + id->lib->filepath); + BLI_assert(0); + return IDWALK_RET_NOP; + } + + if (owner_library_indirect_level >= id->lib->temp_index) { + id->lib->temp_index = owner_library_indirect_level + 1; + *(bool *)cb_data->user_data = true; + } + } + return IDWALK_RET_NOP; +} + +/** Define the `temp_index` of libraries from their highest level of indirect usage. + * + * E.g. if lib_a uses lib_b, lib_c and lib_d, and lib_b also uses lib_d, then lib_a has an index of + * 1, lib_b and lib_c an index of 2, and lib_d an index of 3. */ +static int lib_override_libraries_index_define(Main *bmain) +{ + LISTBASE_FOREACH (Library *, library, &bmain->libraries) { + /* index 0 is reserved for local data. */ + library->temp_index = 1; + } + bool do_continue = true; + while (do_continue) { + do_continue = false; + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + BKE_library_foreach_ID_link( + bmain, id, lib_override_sort_libraries_func, &do_continue, IDWALK_READONLY); + } + FOREACH_MAIN_ID_END; + } - if (!do_continue && library_indirect_level != 0) { - /* We are done with overrides from that level of indirect linking, we can keep going with - * those 'less' indirectly linked now. */ - library_indirect_level--; - do_continue = true; + int library_indirect_level_max = 0; + LISTBASE_FOREACH (Library *, library, &bmain->libraries) { + if (library->temp_index > library_indirect_level_max) { + library_indirect_level_max = library->temp_index; } } + return library_indirect_level_max; +} + +/** + * Detect and handle required resync of overrides data, when relations between reference linked IDs + * have changed. + * + * This is a fairly complex and costly operation, typically it should be called after + * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases. + * + * This function will first detect the remaining cases requiring a resync (namely, either when an + * existing linked ID that did not require to be overridden before now would be, or when new IDs + * are added to the hierarchy). + * + * Then it will handle the resync of necessary IDs (through calls to + * #BKE_lib_override_library_resync). + */ +void BKE_lib_override_library_main_resync(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + ReportList *reports) +{ + /* We use a specific collection to gather/store all 'orphaned' override collections and objects + * generated by re-sync-process. This avoids putting them in scene's master collection. */ +#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS" + Collection *override_resync_residual_storage = BLI_findstring( + &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2); + if (override_resync_residual_storage != NULL && + override_resync_residual_storage->id.lib != NULL) { + override_resync_residual_storage = NULL; + } + if (override_resync_residual_storage == NULL) { + override_resync_residual_storage = BKE_collection_add( + bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME); + /* Hide the collection from viewport and render. */ + override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT | + COLLECTION_RESTRICT_RENDER; + } + + int library_indirect_level = lib_override_libraries_index_define(bmain); + while (library_indirect_level >= 0) { + /* Update overrides from each indirect level separately. */ + lib_override_library_main_resync_on_library_indirect_level(bmain, + scene, + view_layer, + override_resync_residual_storage, + library_indirect_level, + reports); + library_indirect_level--; + } /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ lib_override_library_create_post_process( -- cgit v1.2.3 From 758c115103284bf1657d3b3c74529c551d5d55ad Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 27 May 2021 19:30:23 +0200 Subject: Fix own crash in today's rBf68288a8746f. --- source/blender/blenkernel/intern/lib_override.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index aeef6c04944..0e2317c72de 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1329,7 +1329,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) { CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib); - if (id->lib->temp_index > library_indirect_level) { + if (id->lib != NULL && id->lib->temp_index > library_indirect_level) { CLOG_ERROR( &LOG, "While processing indirect level %d, ID %s from lib %s of indirect level %d detected " @@ -1363,7 +1363,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( id->lib, id_to->name, id_to->lib); - if (id->lib->temp_index > library_indirect_level) { + if (id->lib != NULL && id->lib->temp_index > library_indirect_level) { CLOG_ERROR(&LOG, "While processing indirect level %d, ID %s from lib %s of indirect level %d " "detected " -- cgit v1.2.3 From 0a83f32d79bc70a9769f91c7694c50df15cc4ae8 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 27 May 2021 19:44:53 +0200 Subject: Fix T86465: Annotation Tool is missing in VSE Preview toolbar Added missing topbar in VSE. Also added the Stabilizer options to Topbar for all modes. Reviewed By: mendio, pepeland Maniphest Tasks: T86465 Differential Revision: https://developer.blender.org/D11347 --- .../bl_ui/properties_grease_pencil_common.py | 2 +- .../startup/bl_ui/space_toolsystem_toolbar.py | 24 ++++++++++++++-------- 2 files changed, 17 insertions(+), 9 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 b6d70049bb9..2e89ddcb1d4 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -407,7 +407,7 @@ class AnnotationDataPanel: bl_options = {'DEFAULT_CLOSED'} def draw_header(self, context): - if context.space_data.type not in {'VIEW_3D', 'TOPBAR'}: + if context.space_data.type not in {'VIEW_3D', 'TOPBAR', 'SEQUENCE_EDITOR'}: self.layout.prop(context.space_data, "show_annotation", text="") def draw(self, context): diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 1e52142c85c..8c683c3f205 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -164,7 +164,7 @@ class _defs_annotate: gpl = context.active_annotation_layer if gpl is not None: layout.label(text="Annotation:") - if context.space_data.type == 'VIEW_3D': + if context.space_data.type in ('VIEW_3D', 'SEQUENCE_EDITOR'): if region_type == 'TOOL_HEADER': sub = layout.split(align=True, factor=0.5) sub.ui_units_x = 6.5 @@ -206,14 +206,22 @@ class _defs_annotate: col = layout.row().column(align=True) col.prop(props, "arrowstyle_start", text="Style Start") col.prop(props, "arrowstyle_end", text="End") - elif tool.idname == "builtin.annotate" and region_type != 'TOOL_HEADER': - layout.separator() + elif tool.idname == "builtin.annotate": props = tool.operator_properties("gpencil.annotate") - layout.prop(props, "use_stabilizer", text="Stabilize Stroke") - col = layout.column(align=False) - col.active = props.use_stabilizer - col.prop(props, "stabilizer_radius", text="Radius", slider=True) - col.prop(props, "stabilizer_factor", text="Factor", slider=True) + if region_type == 'TOOL_HEADER': + row = layout.row() + row.prop(props, "use_stabilizer", text="Stabilize Stroke") + subrow = layout.row(align=False) + subrow.active = props.use_stabilizer + subrow.prop(props, "stabilizer_radius", text="Radius", slider=True) + subrow.prop(props, "stabilizer_factor", text="Factor", slider=True) + else: + layout.separator() + layout.prop(props, "use_stabilizer", text="Stabilize Stroke") + col = layout.column(align=False) + col.active = props.use_stabilizer + col.prop(props, "stabilizer_radius", text="Radius", slider=True) + col.prop(props, "stabilizer_factor", text="Factor", slider=True) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) def scribble(*, draw_settings): -- cgit v1.2.3 From 02395fead754293e3ffbee30ecca88022270731a Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Thu, 27 May 2021 21:25:31 -0400 Subject: Docs: Update RNA to User Manual mappings --- release/scripts/modules/rna_manual_reference.py | 88 ++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 8 deletions(-) diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index b6fda8911cc..6d345e87d93 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -58,6 +58,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"), ("bpy.types.fluiddomainsettings.use_collision_border_bottom*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-bottom"), ("bpy.types.fluiddomainsettings.vector_scale_with_magnitude*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-scale-with-magnitude"), + ("bpy.types.spacespreadsheet.display_context_path_collapsed*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-display-context-path-collapsed"), ("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"), @@ -66,16 +67,20 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.use_collision_border_left*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-left"), ("bpy.types.rendersettings_simplify_gpencil_view_modifier*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-modifier"), ("bpy.types.brushgpencilsettings.use_settings_stabilizer*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-stabilizer"), + ("bpy.types.colormanagedsequencercolorspacesettings.name*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings-name"), ("bpy.types.fluiddomainsettings.use_collision_border_top*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-top"), ("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"), ("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"), ("bpy.types.rendersettings.simplify_gpencil_antialiasing*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-antialiasing"), + ("bpy.types.spaceoutliner.use_filter_lib_override_system*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override-system"), ("bpy.types.toolsettings.use_transform_pivot_point_align*", "scene_layout/object/tools/tool_settings.html#bpy-types-toolsettings-use-transform-pivot-point-align"), ("bpy.types.brush.show_multiplane_scrape_planes_preview*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-show-multiplane-scrape-planes-preview"), ("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"), ("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.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"), @@ -117,6 +122,8 @@ url_manual_mapping = ( ("bpy.types.brush.use_cloth_pin_simulation_boundary*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-use-cloth-pin-simulation-boundary"), ("bpy.types.brushgpencilsettings.show_fill_boundary*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-show-fill-boundary"), ("bpy.types.brushgpencilsettings.use_default_eraser*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-use-default-eraser"), + ("bpy.types.colormanagedsequencercolorspacesettings*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings"), + ("bpy.types.colormanagedviewsettings.view_transform*", "render/color_management.html#bpy-types-colormanagedviewsettings-view-transform"), ("bpy.types.cyclesrendersettings.camera_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-camera-cull-margin"), ("bpy.types.fluiddomainsettings.export_manta_script*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-export-manta-script"), ("bpy.types.fluiddomainsettings.fractions_threshold*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-fractions-threshold"), @@ -164,6 +171,7 @@ url_manual_mapping = ( ("bpy.types.rigidbodyconstraint.breaking_threshold*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-breaking-threshold"), ("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.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"), @@ -192,12 +200,14 @@ url_manual_mapping = ( ("bpy.types.materialgpencilstyle.use_fill_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-holdout"), ("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"), ("bpy.types.rigidbodyconstraint.solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-solver-iterations"), + ("bpy.types.spaceoutliner.use_filter_lib_override*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override"), ("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.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"), ("bpy.types.toolsettings.use_keyframe_insert_auto*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-insert-auto"), ("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"), @@ -291,6 +301,7 @@ url_manual_mapping = ( ("bpy.types.volumedisplay.interpolation_method*", "modeling/volumes/properties.html#bpy-types-volumedisplay-interpolation-method"), ("bpy.types.clothsettings.use_pressure_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-use-pressure-volume"), ("bpy.types.clothsettings.vertex_group_intern*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-intern"), + ("bpy.types.colormanagedviewsettings.exposure*", "render/color_management.html#bpy-types-colormanagedviewsettings-exposure"), ("bpy.types.cyclesrendersettings.*dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-rate"), ("bpy.types.fluiddomainsettings.cfl_condition*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-cfl-condition"), ("bpy.types.fluiddomainsettings.show_velocity*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-show-velocity"), @@ -305,7 +316,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodealignrotationtovector*", "modeling/geometry_nodes/point/align_rotation_to_vector.html#bpy-types-geometrynodealignrotationtovector"), ("bpy.types.greasepencil.curve_edit_threshold*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-threshold"), ("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/lineart.html#bpy-types-objectlineart-use-crease-override"), + ("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_file_extension*", "render/output/properties/output.html#bpy-types-rendersettings-use-file-extension"), @@ -410,6 +421,7 @@ url_manual_mapping = ( ("bpy.types.view3doverlay.wireframe_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-wireframe-opacity"), ("bpy.ops.gpencil.active_frames_delete_all*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-active-frames-delete-all"), ("bpy.ops.gpencil.stroke_merge_by_distance*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-merge-by-distance"), + ("bpy.ops.node.collapse_hide_unused_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-collapse-hide-unused-toggle"), ("bpy.ops.object.anim_transforms_to_deltas*", "scene_layout/object/editing/apply.html#bpy-ops-object-anim-transforms-to-deltas"), ("bpy.ops.object.convert_proxy_to_override*", "files/linked_libraries/library_overrides.html#bpy-ops-object-convert-proxy-to-override"), ("bpy.ops.object.modifier_copy_to_selected*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-copy-to-selected"), @@ -421,6 +433,7 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.show_fill*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-show-fill"), ("bpy.types.brushgpencilsettings.uv_random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-uv-random"), ("bpy.types.clothsettings.internal_tension*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-tension"), + ("bpy.types.colormanagedviewsettings.gamma*", "render/color_management.html#bpy-types-colormanagedviewsettings-gamma"), ("bpy.types.compositornodeplanetrackdeform*", "compositing/types/distort/plane_track_deform.html#bpy-types-compositornodeplanetrackdeform"), ("bpy.types.curve.bevel_factor_mapping_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-end"), ("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"), @@ -449,7 +462,7 @@ url_manual_mapping = ( ("bpy.types.nodesocketinterface*.max_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-max-value"), ("bpy.types.nodesocketinterface*.min_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-min-value"), ("bpy.types.nodesocketinterface.hide_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-hide-value"), - ("bpy.types.objectlineart.crease_threshold*", "scene_layout/object/properties/lineart.html#bpy-types-objectlineart-crease-threshold"), + ("bpy.types.objectlineart.crease_threshold*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-crease-threshold"), ("bpy.types.rendersettings.use_compositing*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-compositing"), ("bpy.types.rendersettings.use_placeholder*", "render/output/properties/output.html#bpy-types-rendersettings-use-placeholder"), ("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"), @@ -475,6 +488,7 @@ url_manual_mapping = ( ("bpy.types.brush.multiplane_scrape_angle*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-multiplane-scrape-angle"), ("bpy.types.clothsettings.internal_spring*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-spring"), ("bpy.types.clothsettings.pressure_factor*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-pressure-factor"), + ("bpy.types.colormanagedviewsettings.look*", "render/color_management.html#bpy-types-colormanagedviewsettings-look"), ("bpy.types.compositornodecolorcorrection*", "compositing/types/color/color_correction.html#bpy-types-compositornodecolorcorrection"), ("bpy.types.compositornodemoviedistortion*", "compositing/types/distort/movie_distortion.html#bpy-types-compositornodemoviedistortion"), ("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"), @@ -487,6 +501,7 @@ url_manual_mapping = ( ("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.geometrynodeattributemaprange*", "modeling/geometry_nodes/attribute/attribute_map_range.html#bpy-types-geometrynodeattributemaprange"), ("bpy.types.layercollection.hide_viewport*", "editors/outliner/interface.html#bpy-types-layercollection-hide-viewport"), ("bpy.types.layercollection.indirect_only*", "editors/outliner/interface.html#bpy-types-layercollection-indirect-only"), ("bpy.types.material.use_sss_translucency*", "render/eevee/materials/settings.html#bpy-types-material-use-sss-translucency"), @@ -512,6 +527,7 @@ url_manual_mapping = ( ("bpy.types.view3doverlay.show_wireframes*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-wireframes"), ("bpy.types.view3dshading.background_type*", "editors/3dview/display/shading.html#bpy-types-view3dshading-background-type"), ("bpy.types.workspace.use_filter_by_owner*", "interface/window_system/workspaces.html#bpy-types-workspace-use-filter-by-owner"), + ("bpy.ops.gpencil.image_to_grease_pencil*", "editors/image/editing.html#bpy-ops-gpencil-image-to-grease-pencil"), ("bpy.ops.mesh.vertices_smooth_laplacian*", "modeling/meshes/editing/vertex/laplacian_smooth.html#bpy-ops-mesh-vertices-smooth-laplacian"), ("bpy.ops.object.multires_rebuild_subdiv*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-rebuild-subdiv"), ("bpy.ops.sequencer.select_side_of_frame*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-side-of-frame"), @@ -574,6 +590,7 @@ url_manual_mapping = ( ("bpy.types.brush.texture_overlay_alpha*", "sculpt_paint/brush/cursor.html#bpy-types-brush-texture-overlay-alpha"), ("bpy.types.brushgpencilsettings.random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-random"), ("bpy.types.clothsettings.target_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-target-volume"), + ("bpy.types.colormanageddisplaysettings*", "render/color_management.html#bpy-types-colormanageddisplaysettings"), ("bpy.types.compositornodebilateralblur*", "compositing/types/filter/bilateral_blur.html#bpy-types-compositornodebilateralblur"), ("bpy.types.compositornodedistancematte*", "compositing/types/matte/distance_key.html#bpy-types-compositornodedistancematte"), ("bpy.types.compositornodesetalpha.mode*", "compositing/types/converter/set_alpha.html#bpy-types-compositornodesetalpha-mode"), @@ -659,7 +676,9 @@ url_manual_mapping = ( ("bpy.types.shadernodeambientocclusion*", "render/shader_nodes/input/ao.html#bpy-types-shadernodeambientocclusion"), ("bpy.types.shadernodevolumeabsorption*", "render/shader_nodes/shader/volume_absorption.html#bpy-types-shadernodevolumeabsorption"), ("bpy.types.shadernodevolumeprincipled*", "render/shader_nodes/shader/volume_principled.html#bpy-types-shadernodevolumeprincipled"), + ("bpy.types.spaceoutliner.display_mode*", "editors/outliner/interface.html#bpy-types-spaceoutliner-display-mode"), ("bpy.types.spaceoutliner.filter_state*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-state"), + ("bpy.types.toolsettings.keyframe_type*", "editors/timeline.html#bpy-types-toolsettings-keyframe-type"), ("bpy.types.toolsettings.snap_elements*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-snap-elements"), ("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"), @@ -717,6 +736,7 @@ url_manual_mapping = ( ("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.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"), ("bpy.types.sceneeevee.bokeh_max_size*", "render/eevee/render_settings/depth_of_field.html#bpy-types-sceneeevee-bokeh-max-size"), ("bpy.types.sculpt.detail_type_method*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-type-method"), @@ -741,7 +761,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.select_interior_faces*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-interior-faces"), ("bpy.ops.mesh.select_similar_region*", "modeling/meshes/selecting/similar.html#bpy-ops-mesh-select-similar-region"), ("bpy.ops.mesh.tris_convert_to_quads*", "modeling/meshes/editing/face/triangles_quads.html#bpy-ops-mesh-tris-convert-to-quads"), - ("bpy.ops.node.read_fullsamplelayers*", "interface/controls/nodes/editing.html#bpy-ops-node-read-fullsamplelayers"), + ("bpy.ops.node.active_preview_toggle*", "modeling/geometry_nodes/introduction.html#bpy-ops-node-active-preview-toggle"), ("bpy.ops.object.datalayout_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data_layout.html#bpy-ops-object-datalayout-transfer"), ("bpy.ops.object.multires_base_apply*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-base-apply"), ("bpy.ops.object.randomize_transform*", "scene_layout/object/editing/transform/randomize.html#bpy-ops-object-randomize-transform"), @@ -751,6 +771,7 @@ url_manual_mapping = ( ("bpy.ops.object.vertex_group_remove*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-remove"), ("bpy.ops.object.vertex_group_smooth*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-smooth"), ("bpy.ops.outliner.collection_enable*", "editors/outliner/editing.html#bpy-ops-outliner-collection-enable"), + ("bpy.ops.palette.extract_from_image*", "editors/image/editing.html#bpy-ops-palette-extract-from-image"), ("bpy.ops.pose.user_transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-user-transforms-clear"), ("bpy.ops.poselib.browse_interactive*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-browse-interactive"), ("bpy.ops.sculpt.set_persistent_base*", "sculpt_paint/sculpting/tools/layer.html#bpy-ops-sculpt-set-persistent-base"), @@ -765,6 +786,7 @@ url_manual_mapping = ( ("bpy.types.brush.use_cursor_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-cursor-overlay"), ("bpy.types.camera.show_passepartout*", "render/cameras.html#bpy-types-camera-show-passepartout"), ("bpy.types.collection.lineart_usage*", "scene_layout/collections/properties.html#bpy-types-collection-lineart-usage"), + ("bpy.types.colormanagedviewsettings*", "render/color_management.html#bpy-types-colormanagedviewsettings"), ("bpy.types.compositornodebokehimage*", "compositing/types/input/bokeh_image.html#bpy-types-compositornodebokehimage"), ("bpy.types.compositornodecolormatte*", "compositing/types/matte/color_key.html#bpy-types-compositornodecolormatte"), ("bpy.types.compositornodecolorspill*", "compositing/types/matte/color_spill.html#bpy-types-compositornodecolorspill"), @@ -796,6 +818,7 @@ url_manual_mapping = ( ("bpy.types.shadernodebsdfrefraction*", "render/shader_nodes/shader/refraction.html#bpy-types-shadernodebsdfrefraction"), ("bpy.types.shadernodeoutputmaterial*", "render/shader_nodes/output/material.html#bpy-types-shadernodeoutputmaterial"), ("bpy.types.shadernodetexenvironment*", "render/shader_nodes/textures/environment.html#bpy-types-shadernodetexenvironment"), + ("bpy.types.spacesequenceeditor.show*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show"), ("bpy.types.spaceuveditor.uv_opacity*", "editors/uv/overlays.html#bpy-types-spaceuveditor-uv-opacity"), ("bpy.types.subdividegpencilmodifier*", "grease_pencil/modifiers/generate/subdivide.html#bpy-types-subdividegpencilmodifier"), ("bpy.types.thicknessgpencilmodifier*", "grease_pencil/modifiers/deform/thickness.html#bpy-types-thicknessgpencilmodifier"), @@ -867,6 +890,7 @@ url_manual_mapping = ( ("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.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"), ("bpy.types.shadernodeeeveespecular*", "render/shader_nodes/shader/specular_bsdf.html#bpy-types-shadernodeeeveespecular"), @@ -904,6 +928,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.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"), @@ -947,7 +972,7 @@ url_manual_mapping = ( ("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.latticegpencilmodifier*", "grease_pencil/modifiers/deform/lattice.html#bpy-types-latticegpencilmodifier"), - ("bpy.types.lineartgpencilmodifier*", "grease_pencil/modifiers/generate/lineart.html#bpy-types-lineartgpencilmodifier"), + ("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.objectsolverconstraint*", "animation/constraints/motion_tracking/object_solver.html#bpy-types-objectsolverconstraint"), ("bpy.types.opacitygpencilmodifier*", "grease_pencil/modifiers/color/opacity.html#bpy-types-opacitygpencilmodifier"), @@ -978,6 +1003,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.stroke_separate*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-separate"), ("bpy.ops.gpencil.stroke_simplify*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-simplify"), ("bpy.ops.graph.snap_cursor_value*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-snap-cursor-value"), + ("bpy.ops.image.save_all_modified*", "editors/image/editing.html#bpy-ops-image-save-all-modified"), ("bpy.ops.mesh.extrude_edges_move*", "modeling/meshes/editing/edge/extrude_edges.html#bpy-ops-mesh-extrude-edges-move"), ("bpy.ops.mesh.extrude_faces_move*", "modeling/meshes/editing/face/extrude_individual_faces.html#bpy-ops-mesh-extrude-faces-move"), ("bpy.ops.mesh.faces_shade_smooth*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-smooth"), @@ -986,6 +1012,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.primitive_cube_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-cube-add"), ("bpy.ops.mesh.primitive_grid_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-grid-add"), ("bpy.ops.mesh.subdivide_edgering*", "modeling/meshes/editing/edge/subdivide_edge_ring.html#bpy-ops-mesh-subdivide-edgering"), + ("bpy.ops.node.hide_socket_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-hide-socket-toggle"), ("bpy.ops.node.tree_socket_remove*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-remove"), ("bpy.ops.object.constraints_copy*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraints-copy"), ("bpy.ops.object.gpencil_modifier*", "grease_pencil/modifiers/index.html#bpy-ops-object-gpencil-modifier"), @@ -996,6 +1023,8 @@ url_manual_mapping = ( ("bpy.ops.object.vertex_group_add*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-add"), ("bpy.ops.object.vertex_group_fix*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-fix"), ("bpy.ops.outliner.collection_new*", "editors/outliner/editing.html#bpy-ops-outliner-collection-new"), + ("bpy.ops.outliner.show_hierarchy*", "editors/outliner/editing.html#bpy-ops-outliner-show-hierarchy"), + ("bpy.ops.outliner.show_one_level*", "editors/outliner/editing.html#bpy-ops-outliner-show-one-level"), ("bpy.ops.paint.brush_colors_flip*", "sculpt_paint/texture_paint/tool_settings/brush_settings.html#bpy-ops-paint-brush-colors-flip"), ("bpy.ops.paint.weight_from_bones*", "sculpt_paint/weight_paint/editing.html#bpy-ops-paint-weight-from-bones"), ("bpy.ops.poselib.action_sanitize*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-action-sanitize"), @@ -1010,6 +1039,7 @@ url_manual_mapping = ( ("bpy.ops.uv.shortest_path_select*", "editors/uv/selecting.html#bpy-ops-uv-shortest-path-select"), ("bpy.ops.wm.operator_cheat_sheet*", "advanced/operators.html#bpy-ops-wm-operator-cheat-sheet"), ("bpy.ops.wm.previews_batch_clear*", "files/blend/previews.html#bpy-ops-wm-previews-batch-clear"), + ("bpy.ops.wm.recover_last_session*", "files/blend/open_save.html#bpy-ops-wm-recover-last-session"), ("bpy.types.armature.use_mirror_x*", "animation/armatures/bones/tools/tool_settings.html#bpy-types-armature-use-mirror-x"), ("bpy.types.bakesettings.normal_b*", "render/cycles/baking.html#bpy-types-bakesettings-normal-b"), ("bpy.types.bakesettings.normal_g*", "render/cycles/baking.html#bpy-types-bakesettings-normal-g"), @@ -1045,6 +1075,7 @@ url_manual_mapping = ( ("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.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.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"), @@ -1105,17 +1136,20 @@ url_manual_mapping = ( ("bpy.types.compositornoderotate*", "compositing/types/distort/rotate.html#bpy-types-compositornoderotate"), ("bpy.types.compositornodeviewer*", "compositing/types/output/viewer.html#bpy-types-compositornodeviewer"), ("bpy.types.constraint.influence*", "animation/constraints/interface/common.html#bpy-types-constraint-influence"), + ("bpy.types.curve.use_path_clamp*", "modeling/curves/properties/path_animation.html#bpy-types-curve-use-path-clamp"), ("bpy.types.datatransfermodifier*", "modeling/modifiers/modify/data_transfer.html#bpy-types-datatransfermodifier"), ("bpy.types.dynamicpaintmodifier*", "physics/dynamic_paint/index.html#bpy-types-dynamicpaintmodifier"), ("bpy.types.ffmpegsettings.audio*", "render/output/properties/output.html#bpy-types-ffmpegsettings-audio"), ("bpy.types.followpathconstraint*", "animation/constraints/relationship/follow_path.html#bpy-types-followpathconstraint"), ("bpy.types.gaussianblursequence*", "video_editing/sequencer/strips/effects/blur.html#bpy-types-gaussianblursequence"), + ("bpy.types.geometrynodeboundbox*", "modeling/geometry_nodes/geometry/bounding_box.html#bpy-types-geometrynodeboundbox"), ("bpy.types.geometrynodemeshcone*", "modeling/geometry_nodes/mesh_primitives/cone.html#bpy-types-geometrynodemeshcone"), ("bpy.types.geometrynodemeshcube*", "modeling/geometry_nodes/mesh_primitives/cube.html#bpy-types-geometrynodemeshcube"), ("bpy.types.geometrynodemeshgrid*", "modeling/geometry_nodes/mesh_primitives/grid.html#bpy-types-geometrynodemeshgrid"), ("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.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"), @@ -1124,6 +1158,7 @@ url_manual_mapping = ( ("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"), ("bpy.types.preferencesfilepaths*", "editors/preferences/file_paths.html#bpy-types-preferencesfilepaths"), + ("bpy.types.rigidbodyobject.mass*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-mass"), ("bpy.types.scene.background_set*", "scene_layout/scene/properties.html#bpy-types-scene-background-set"), ("bpy.types.sequence.frame_start*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-frame-start"), ("bpy.types.shadernodebackground*", "render/shader_nodes/shader/background.html#bpy-types-shadernodebackground"), @@ -1156,6 +1191,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.stroke_sample*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-sample"), ("bpy.ops.gpencil.stroke_smooth*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-stroke-smooth"), ("bpy.ops.graph.keyframe_insert*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-keyframe-insert"), + ("bpy.ops.image.read_viewlayers*", "editors/image/editing.html#bpy-ops-image-read-viewlayers"), ("bpy.ops.mesh.blend_from_shape*", "modeling/meshes/editing/vertex/blend_shape.html#bpy-ops-mesh-blend-from-shape"), ("bpy.ops.mesh.dissolve_limited*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-limited"), ("bpy.ops.mesh.face_make_planar*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-face-make-planar"), @@ -1163,6 +1199,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.faces_shade_flat*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-flat"), ("bpy.ops.mesh.paint_mask_slice*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-mesh-paint-mask-slice"), ("bpy.ops.mesh.select_ungrouped*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-ungrouped"), + ("bpy.ops.node.delete_reconnect*", "interface/controls/nodes/editing.html#bpy-ops-node-delete-reconnect"), ("bpy.ops.node.tree_path_parent*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-path-parent"), ("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"), @@ -1219,7 +1256,7 @@ url_manual_mapping = ( ("bpy.types.mesh.use_paint_mask*", "sculpt_paint/brush/introduction.html#bpy-types-mesh-use-paint-mask"), ("bpy.types.movietrackingcamera*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera"), ("bpy.types.object.display_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-type"), - ("bpy.types.objectlineart.usage*", "scene_layout/object/properties/lineart.html#bpy-types-objectlineart-usage"), + ("bpy.types.objectlineart.usage*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-usage"), ("bpy.types.particledupliweight*", "physics/particles/emitter/vertex_groups.html#bpy-types-particledupliweight"), ("bpy.types.poseboneconstraints*", "animation/armatures/posing/bone_constraints/index.html#bpy-types-poseboneconstraints"), ("bpy.types.rigidbodyconstraint*", "physics/rigid_body/constraints/index.html#bpy-types-rigidbodyconstraint"), @@ -1270,6 +1307,8 @@ url_manual_mapping = ( ("bpy.ops.mesh.delete_edgeloop*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete-edgeloop"), ("bpy.ops.mesh.vertices_smooth*", "modeling/meshes/editing/vertex/smooth_vertices.html#bpy-ops-mesh-vertices-smooth"), ("bpy.ops.nla.make_single_user*", "editors/nla/editing.html#bpy-ops-nla-make-single-user"), + ("bpy.ops.node.clipboard_paste*", "interface/controls/nodes/editing.html#bpy-ops-node-clipboard-paste"), + ("bpy.ops.node.node_copy_color*", "interface/controls/nodes/sidebar.html#bpy-ops-node-node-copy-color"), ("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"), @@ -1277,6 +1316,8 @@ url_manual_mapping = ( ("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"), ("bpy.ops.object.select_random*", "scene_layout/object/selecting.html#bpy-ops-object-select-random"), + ("bpy.ops.object.transfer_mode*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-object-transfer-mode"), + ("bpy.ops.outliner.show_active*", "editors/outliner/editing.html#bpy-ops-outliner-show-active"), ("bpy.ops.paint.add_simple_uvs*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-ops-paint-add-simple-uvs"), ("bpy.ops.pose.rigify_generate*", "addons/rigging/rigify/basics.html#bpy-ops-pose-rigify-generate"), ("bpy.ops.preferences.autoexec*", "editors/preferences/save_load.html#bpy-ops-preferences-autoexec"), @@ -1293,6 +1334,7 @@ url_manual_mapping = ( ("bpy.ops.uv.project_from_view*", "modeling/meshes/editing/uv.html#bpy-ops-uv-project-from-view"), ("bpy.ops.wm.blenderkit_logout*", "addons/3d_view/blenderkit.html#bpy-ops-wm-blenderkit-logout"), ("bpy.ops.wm.memory_statistics*", "advanced/operators.html#bpy-ops-wm-memory-statistics"), + ("bpy.ops.wm.recover_auto_save*", "files/blend/open_save.html#bpy-ops-wm-recover-auto-save"), ("bpy.types.adjustmentsequence*", "video_editing/sequencer/strips/adjustment.html#bpy-types-adjustmentsequence"), ("bpy.types.alphaundersequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaundersequence"), ("bpy.types.armature.show_axes*", "animation/armatures/properties/display.html#bpy-types-armature-show-axes"), @@ -1351,6 +1393,7 @@ url_manual_mapping = ( ("bpy.ops.anim.keyframe_clear*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-clear"), ("bpy.ops.armature.flip_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-flip-names"), ("bpy.ops.armature.select_all*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-all"), + ("bpy.ops.clip.average_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-average-tracks"), ("bpy.ops.clip.refine_markers*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-refine-markers"), ("bpy.ops.clip.select_grouped*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-grouped"), ("bpy.ops.clip.track_to_empty*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-track-to-empty"), @@ -1363,6 +1406,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.stroke_join*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-join"), ("bpy.ops.gpencil.stroke_trim*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-trim"), ("bpy.ops.gpencil.trace_image*", "grease_pencil/modes/object/trace_image.html#bpy-ops-gpencil-trace-image"), + ("bpy.ops.image.external_edit*", "editors/image/editing.html#bpy-ops-image-external-edit"), ("bpy.ops.mesh.colors_reverse*", "modeling/meshes/editing/face/face_data.html#bpy-ops-mesh-colors-reverse"), ("bpy.ops.mesh.dissolve_edges*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-edges"), ("bpy.ops.mesh.dissolve_faces*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-faces"), @@ -1377,6 +1421,10 @@ url_manual_mapping = ( ("bpy.ops.mesh.smooth_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-smooth-normals"), ("bpy.ops.nla.action_pushdown*", "editors/nla/tracks.html#bpy-ops-nla-action-pushdown"), ("bpy.ops.nla.tweakmode_enter*", "editors/nla/editing.html#bpy-ops-nla-tweakmode-enter"), + ("bpy.ops.node.clipboard_copy*", "interface/controls/nodes/editing.html#bpy-ops-node-clipboard-copy"), + ("bpy.ops.node.duplicate_move*", "interface/controls/nodes/editing.html#bpy-ops-node-duplicate-move"), + ("bpy.ops.node.options_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-options-toggle"), + ("bpy.ops.node.preview_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-preview-toggle"), ("bpy.ops.object.origin_clear*", "scene_layout/object/editing/clear.html#bpy-ops-object-origin-clear"), ("bpy.ops.object.parent_clear*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-clear"), ("bpy.ops.object.shade_smooth*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-smooth"), @@ -1397,6 +1445,7 @@ url_manual_mapping = ( ("bpy.ops.uv.minimize_stretch*", "modeling/meshes/uv/editing.html#bpy-ops-uv-minimize-stretch"), ("bpy.ops.uv.select_edge_ring*", "editors/uv/selecting.html#bpy-ops-uv-select-edge-ring"), ("bpy.ops.wm.blenderkit_login*", "addons/3d_view/blenderkit.html#bpy-ops-wm-blenderkit-login"), + ("bpy.ops.wm.save_as_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-as-mainfile"), ("bpy.types.alphaoversequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaoversequence"), ("bpy.types.armatureeditbones*", "animation/armatures/bones/editing/index.html#bpy-types-armatureeditbones"), ("bpy.types.brush.pose_offset*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-offset"), @@ -1453,7 +1502,7 @@ url_manual_mapping = ( ("bpy.types.viewlayer.use_sky*", "render/layers/introduction.html#bpy-types-viewlayer-use-sky"), ("bpy.types.wireframemodifier*", "modeling/modifiers/generate/wireframe.html#bpy-types-wireframemodifier"), ("bpy.types.worldmistsettings*", "render/cycles/world_settings.html#bpy-types-worldmistsettings"), - ("bpy.ops.anim.channels_move*", "editors/nla/editing.html#bpy-ops-anim-channels-move"), + ("bpy.ops.anim.channels_move*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-move"), ("bpy.ops.buttons.toggle_pin*", "editors/properties_editor.html#bpy-ops-buttons-toggle-pin"), ("bpy.ops.clip.filter_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-filter-tracks"), ("bpy.ops.clip.select_circle*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-circle"), @@ -1636,11 +1685,15 @@ url_manual_mapping = ( ("bpy.ops.mesh.edge_rotate*", "modeling/meshes/editing/edge/rotate_edge.html#bpy-ops-mesh-edge-rotate"), ("bpy.ops.mesh.unsubdivide*", "modeling/meshes/editing/edge/unsubdivide.html#bpy-ops-mesh-unsubdivide"), ("bpy.ops.mesh.uvs_reverse*", "modeling/meshes/uv/editing.html#bpy-ops-mesh-uvs-reverse"), + ("bpy.ops.node.hide_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-hide-toggle"), + ("bpy.ops.node.mute_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-mute-toggle"), ("bpy.ops.object.hide_view*", "scene_layout/object/editing/show_hide.html#bpy-ops-object-hide-view"), ("bpy.ops.object.track_set*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-track-set"), ("bpy.ops.pose.scale_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-scale-clear"), ("bpy.ops.poselib.pose_add*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-add"), ("bpy.ops.scene.view_layer*", "render/layers/introduction.html#bpy-ops-scene-view-layer"), + ("bpy.ops.screen.redo_last*", "interface/undo_redo.html#bpy-ops-screen-redo-last"), + ("bpy.ops.sculpt.mask_init*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-init"), ("bpy.ops.sequencer.delete*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-delete"), ("bpy.ops.sequencer.reload*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reload"), ("bpy.ops.sequencer.unlock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unlock"), @@ -1653,7 +1706,9 @@ url_manual_mapping = ( ("bpy.ops.uv.snap_selected*", "modeling/meshes/uv/editing.html#bpy-ops-uv-snap-selected"), ("bpy.ops.view3d.localview*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview"), ("bpy.ops.view3d.view_axis*", "editors/3dview/navigate/viewpoint.html#bpy-ops-view3d-view-axis"), + ("bpy.ops.wm.open_mainfile*", "files/blend/open_save.html#bpy-ops-wm-open-mainfile"), ("bpy.ops.wm.owner_disable*", "interface/window_system/workspaces.html#bpy-ops-wm-owner-disable"), + ("bpy.ops.wm.save_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-mainfile"), ("bpy.types.bone.show_wire*", "animation/armatures/bones/properties/display.html#bpy-types-bone-show-wire"), ("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"), @@ -1708,6 +1763,7 @@ url_manual_mapping = ( ("bpy.ops.nla.clear_scale*", "editors/nla/editing.html#bpy-ops-nla-clear-scale"), ("bpy.ops.nla.mute_toggle*", "editors/nla/editing.html#bpy-ops-nla-mute-toggle"), ("bpy.ops.node.group_make*", "interface/controls/nodes/groups.html#bpy-ops-node-group-make"), + ("bpy.ops.node.links_mute*", "interface/controls/nodes/editing.html#bpy-ops-node-links-mute"), ("bpy.ops.object.armature*", "animation/armatures/index.html#bpy-ops-object-armature"), ("bpy.ops.object.face_map*", "modeling/meshes/properties/object_data.html#bpy-ops-object-face-map"), ("bpy.ops.object.join_uvs*", "scene_layout/object/editing/link_transfer/copy_uvmaps.html#bpy-ops-object-join-uvs"), @@ -1749,7 +1805,7 @@ url_manual_mapping = ( ("bpy.types.nlastrip.name*", "editors/nla/sidebar.html#bpy-types-nlastrip-name"), ("bpy.types.nodesmodifier*", "modeling/modifiers/generate/geometry_nodes.html#bpy-types-nodesmodifier"), ("bpy.types.object.parent*", "scene_layout/object/editing/parent.html#bpy-types-object-parent"), - ("bpy.types.objectlineart*", "scene_layout/object/properties/lineart.html#bpy-types-objectlineart"), + ("bpy.types.objectlineart*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart"), ("bpy.types.oceanmodifier*", "modeling/modifiers/physics/ocean.html#bpy-types-oceanmodifier"), ("bpy.types.particlebrush*", "physics/particles/mode.html#bpy-types-particlebrush"), ("bpy.types.scene.gravity*", "physics/forces/gravity.html#bpy-types-scene-gravity"), @@ -1787,6 +1843,8 @@ url_manual_mapping = ( ("bpy.ops.mesh.polybuild*", "modeling/meshes/tools/poly_build.html#bpy-ops-mesh-polybuild"), ("bpy.ops.mesh.subdivide*", "modeling/meshes/editing/edge/subdivide.html#bpy-ops-mesh-subdivide"), ("bpy.ops.mesh.wireframe*", "modeling/meshes/editing/face/wireframe.html#bpy-ops-mesh-wireframe"), + ("bpy.ops.node.link_make*", "interface/controls/nodes/editing.html#bpy-ops-node-link-make"), + ("bpy.ops.node.links_cut*", "interface/controls/nodes/editing.html#bpy-ops-node-links-cut"), ("bpy.ops.object.convert*", "scene_layout/object/editing/convert.html#bpy-ops-object-convert"), ("bpy.ops.object.gpencil*", "grease_pencil/index.html#bpy-ops-object-gpencil"), ("bpy.ops.object.speaker*", "render/output/audio/speaker.html#bpy-ops-object-speaker"), @@ -1808,7 +1866,6 @@ url_manual_mapping = ( ("bpy.types.blendtexture*", "render/materials/legacy_textures/types/blend.html#bpy-types-blendtexture"), ("bpy.types.brush.height*", "sculpt_paint/sculpting/tools/layer.html#bpy-types-brush-height"), ("bpy.types.castmodifier*", "modeling/modifiers/deform/cast.html#bpy-types-castmodifier"), - ("bpy.types.colormanaged*", "render/color_management.html#bpy-types-colormanaged"), ("bpy.types.curve.offset*", "modeling/curves/properties/geometry.html#bpy-types-curve-offset"), ("bpy.types.geometrynode*", "modeling/geometry_nodes/index.html#bpy-types-geometrynode"), ("bpy.types.glowsequence*", "video_editing/sequencer/strips/effects/glow.html#bpy-types-glowsequence"), @@ -1847,6 +1904,8 @@ url_manual_mapping = ( ("bpy.ops.clip.set_axis*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-axis"), ("bpy.ops.gpencil.paste*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-paste"), ("bpy.ops.image.project*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-ops-image-project"), + ("bpy.ops.image.replace*", "editors/image/editing.html#bpy-ops-image-replace"), + ("bpy.ops.image.save_as*", "editors/image/editing.html#bpy-ops-image-save-as"), ("bpy.ops.material.copy*", "render/materials/assignment.html#bpy-ops-material-copy"), ("bpy.ops.mesh.decimate*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-decimate"), ("bpy.ops.mesh.dissolve*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve"), @@ -1899,6 +1958,10 @@ url_manual_mapping = ( ("bpy.ops.graph.sample*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-sample"), ("bpy.ops.graph.smooth*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-smooth"), ("bpy.ops.graph.unbake*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-unbake"), + ("bpy.ops.image.invert*", "editors/image/editing.html#bpy-ops-image-invert"), + ("bpy.ops.image.reload*", "editors/image/editing.html#bpy-ops-image-reload"), + ("bpy.ops.image.resize*", "editors/image/editing.html#bpy-ops-image-resize"), + ("bpy.ops.image.unpack*", "editors/image/editing.html#bpy-ops-image-unpack"), ("bpy.ops.material.new*", "render/materials/assignment.html#bpy-ops-material-new"), ("bpy.ops.object.align*", "scene_layout/object/editing/transform/align_objects.html#bpy-ops-object-align"), ("bpy.ops.object.empty*", "modeling/empties.html#bpy-ops-object-empty"), @@ -1913,6 +1976,8 @@ url_manual_mapping = ( ("bpy.types.constraint*", "animation/constraints/index.html#bpy-types-constraint"), ("bpy.types.imagepaint*", "sculpt_paint/texture_paint/index.html#bpy-types-imagepaint"), ("bpy.types.lightprobe*", "render/eevee/light_probes/index.html#bpy-types-lightprobe"), + ("bpy.types.node.color*", "interface/controls/nodes/sidebar.html#bpy-types-node-color"), + ("bpy.types.node.label*", "interface/controls/nodes/sidebar.html#bpy-types-node-label"), ("bpy.types.nodesocket*", "interface/controls/nodes/parts.html#bpy-types-nodesocket"), ("bpy.types.paint.tile*", "sculpt_paint/texture_paint/tool_settings/tiling.html#bpy-types-paint-tile"), ("bpy.types.pointcache*", "physics/baking.html#bpy-types-pointcache"), @@ -1927,6 +1992,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.bisect*", "modeling/meshes/editing/mesh/bisect.html#bpy-ops-mesh-bisect"), ("bpy.ops.mesh.delete*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete"), ("bpy.ops.nla.move_up*", "editors/nla/editing.html#bpy-ops-nla-move-up"), + ("bpy.ops.node.delete*", "interface/controls/nodes/editing.html#bpy-ops-node-delete"), ("bpy.ops.object.bake*", "render/cycles/baking.html#bpy-ops-object-bake"), ("bpy.ops.object.hook*", "modeling/meshes/editing/vertex/hooks.html#bpy-ops-object-hook"), ("bpy.ops.object.join*", "scene_layout/object/editing/join.html#bpy-ops-object-join"), @@ -1946,6 +2012,7 @@ url_manual_mapping = ( ("bpy.types.fmodifier*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifier"), ("bpy.types.freestyle*", "render/freestyle/index.html#bpy-types-freestyle"), ("bpy.types.movieclip*", "movie_clip/index.html#bpy-types-movieclip"), + ("bpy.types.node.name*", "interface/controls/nodes/sidebar.html#bpy-types-node-name"), ("bpy.types.nodeframe*", "interface/controls/nodes/frame.html#bpy-types-nodeframe"), ("bpy.types.nodegroup*", "interface/controls/nodes/groups.html#bpy-types-nodegroup"), ("bpy.types.spotlight*", "render/lights/light_object.html#bpy-types-spotlight"), @@ -1963,6 +2030,10 @@ url_manual_mapping = ( ("bpy.ops.graph.copy*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-copy"), ("bpy.ops.graph.hide*", "editors/graph_editor/channels.html#bpy-ops-graph-hide"), ("bpy.ops.graph.snap*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-snap"), + ("bpy.ops.image.flip*", "editors/image/editing.html#bpy-ops-image-flip"), + ("bpy.ops.image.open*", "editors/image/editing.html#bpy-ops-image-open"), + ("bpy.ops.image.pack*", "editors/image/editing.html#bpy-ops-image-pack"), + ("bpy.ops.image.save*", "editors/image/editing.html#bpy-ops-image-save"), ("bpy.ops.image.tile*", "modeling/meshes/uv/workflows/udims.html#bpy-ops-image-tile"), ("bpy.ops.mesh.bevel*", "modeling/meshes/editing/edge/bevel.html#bpy-ops-mesh-bevel"), ("bpy.ops.mesh.inset*", "modeling/meshes/editing/face/inset_faces.html#bpy-ops-mesh-inset"), @@ -1994,6 +2065,7 @@ url_manual_mapping = ( ("bpy.types.spacenla*", "editors/nla/index.html#bpy-types-spacenla"), ("bpy.types.sunlight*", "render/lights/light_object.html#bpy-types-sunlight"), ("bpy.ops.clip.open*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-open"), + ("bpy.ops.image.new*", "editors/image/editing.html#bpy-ops-image-new"), ("bpy.ops.mesh.fill*", "modeling/meshes/editing/face/fill.html#bpy-ops-mesh-fill"), ("bpy.ops.mesh.poke*", "modeling/meshes/editing/face/poke_faces.html#bpy-ops-mesh-poke"), ("bpy.ops.mesh.spin*", "modeling/meshes/tools/spin.html#bpy-ops-mesh-spin"), -- cgit v1.2.3 From 6b03621c018acc3b343caa1d8d2aad746fcffc08 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 28 May 2021 08:16:26 +0200 Subject: DrawManager: Use Compute Shader to Update Hair. This patch will use compute shaders to create the VBO for hair. The previous implementation uses transform feedback. Timings before: between 0.000069s and 0.000362s. Timings after: between 0.000032s and 0.000092s. Speedup isn't noticeable by end-users. The patch is used to test the new compute shader pipeline and integrate it with the draw manager. Allowing EEVEE, Workbench and other draw engines to use compute shaders with the introduction of `DRW_shgroup_call_compute` and `DRW_shgroup_vertex_buffer`. Future improvements are possible by generating the index buffer of hair directly on the GPU. NOTE: that compute shaders aren't supported by Apple and still use the transform feedback workaround. Reviewed By: fclem Differential Revision: https://developer.blender.org/D11057 --- source/blender/draw/CMakeLists.txt | 1 + source/blender/draw/intern/DRW_render.h | 7 + source/blender/draw/intern/draw_cache_impl_hair.c | 3 +- source/blender/draw/intern/draw_hair.c | 200 ++++++++++++++++----- source/blender/draw/intern/draw_manager.h | 13 ++ source/blender/draw/intern/draw_manager_data.c | 36 ++++ source/blender/draw/intern/draw_manager_exec.c | 10 ++ .../draw/intern/shaders/common_hair_lib.glsl | 78 +++++++- .../intern/shaders/common_hair_refine_comp.glsl | 24 +++ .../intern/shaders/common_hair_refine_vert.glsl | 45 +---- source/blender/gpu/GPU_capabilities.h | 2 + source/blender/gpu/intern/gpu_capabilities.cc | 10 ++ .../blender/gpu/intern/gpu_capabilities_private.hh | 2 + source/blender/gpu/opengl/gl_backend.cc | 8 + 14 files changed, 344 insertions(+), 95 deletions(-) create mode 100644 source/blender/draw/intern/shaders/common_hair_refine_comp.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index afb0f613290..0541aa982f3 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -322,6 +322,7 @@ data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC) data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC) +data_to_c_simple(intern/shaders/common_hair_refine_comp.glsl SRC) data_to_c_simple(intern/shaders/common_math_lib.glsl SRC) data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC) data_to_c_simple(intern/shaders/common_view_lib.glsl SRC) diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 2545cfa65dc..5071658fd82 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -438,6 +438,10 @@ void DRW_shgroup_call_range( void DRW_shgroup_call_instance_range( DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct); +void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, + int groups_x_len, + int groups_y_len, + int groups_z_len); void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count); void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count); void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count); @@ -575,6 +579,9 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, const char *name, const float (*value)[4], int arraysize); +void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf *vertex_buffer); bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup); diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c index fd28ac00186..6424b21666d 100644 --- a/source/blender/draw/intern/draw_cache_impl_hair.c +++ b/source/blender/draw/intern/draw_cache_impl_hair.c @@ -243,7 +243,8 @@ static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *c GPUVertFormat format = {0}; GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format(&format); + cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format_ex(&format, + GPU_USAGE_DEVICE_ONLY); /* Create a destination buffer for the transform feedback. Sized appropriately */ /* Those are points! not line segments. */ diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index bca227a24e2..bf3b10bccd3 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -36,15 +36,28 @@ #include "BKE_duplilist.h" #include "GPU_batch.h" +#include "GPU_capabilities.h" +#include "GPU_compute.h" #include "GPU_shader.h" +#include "GPU_texture.h" #include "GPU_vertex_buffer.h" #include "draw_hair_private.h" #ifndef __APPLE__ # define USE_TRANSFORM_FEEDBACK +# define USE_COMPUTE_SHADERS #endif +BLI_INLINE bool drw_hair_use_compute_shaders(void) +{ +#ifdef USE_COMPUTE_SHADERS + return GPU_compute_shader_support(); +#else + return false; +#endif +} + typedef enum ParticleRefineShader { PART_REFINE_CATMULL_ROM = 0, PART_REFINE_MAX_SHADER, @@ -71,38 +84,89 @@ static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in t extern char datatoc_common_hair_lib_glsl[]; extern char datatoc_common_hair_refine_vert_glsl[]; +extern char datatoc_common_hair_refine_comp_glsl[]; extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; -static GPUShader *hair_refine_shader_get(ParticleRefineShader sh) +/* TODO(jbakker): move shader creation to `draw_shaders` and add test cases. */ +/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OSs. Currently the + * APPLE codepath does not compile on other platforms and vice versa. */ +#ifdef USE_COMPUTE_SHADERS +static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement)) { - if (g_refine_shaders[sh]) { - return g_refine_shaders[sh]; - } - - char *vert_with_lib = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); + GPUShader *sh = NULL; + sh = GPU_shader_create_compute(datatoc_common_hair_refine_comp_glsl, + datatoc_common_hair_lib_glsl, + "#define HAIR_PHASE_SUBDIV\n", + __func__); + return sh; +} +#endif #ifdef USE_TRANSFORM_FEEDBACK +static GPUShader *hair_refine_shader_transform_feedback_create( + ParticleRefineShader UNUSED(refinement)) +{ + GPUShader *sh = NULL; + + char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, + datatoc_common_hair_refine_vert_glsl); const char *var_names[1] = {"finalColor"}; - g_refine_shaders[sh] = DRW_shader_create_with_transform_feedback( - vert_with_lib, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); -#else - g_refine_shaders[sh] = DRW_shader_create(vert_with_lib, - NULL, - datatoc_gpu_shader_3D_smooth_color_frag_glsl, - "#define blender_srgb_to_framebuffer_space(a) a\n" - "#define HAIR_PHASE_SUBDIV\n" - "#define TF_WORKAROUND\n"); + sh = DRW_shader_create_with_transform_feedback( + shader_src, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1); + MEM_freeN(shader_src); + + return sh; +} #endif - MEM_freeN(vert_with_lib); +static GPUShader *hair_refine_shader_transform_feedback_workaround_create( + ParticleRefineShader UNUSED(refinement)) +{ + GPUShader *sh = NULL; + + char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, + datatoc_common_hair_refine_vert_glsl); + sh = DRW_shader_create(shader_src, + NULL, + datatoc_gpu_shader_3D_smooth_color_frag_glsl, + "#define blender_srgb_to_framebuffer_space(a) a\n" + "#define HAIR_PHASE_SUBDIV\n" + "#define TF_WORKAROUND\n"); + MEM_freeN(shader_src); + + return sh; +} + +static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement) +{ + if (g_refine_shaders[refinement]) { + return g_refine_shaders[refinement]; + } + +#ifdef USE_COMPUTE_SHADERS + if (drw_hair_use_compute_shaders()) { + g_refine_shaders[refinement] = hair_refine_shader_compute_create(refinement); + if (g_refine_shaders[refinement]) { + return g_refine_shaders[refinement]; + } + } +#endif + +#ifdef USE_TRANSFORM_FEEDBACK + g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_create(refinement); + if (g_refine_shaders[refinement]) { + return g_refine_shaders[refinement]; + } +#endif - return g_refine_shaders[sh]; + g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_workaround_create( + refinement); + return g_refine_shaders[refinement]; } void DRW_hair_init(void) { -#ifdef USE_TRANSFORM_FEEDBACK +#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) g_tf_pass = DRW_pass_create("Update Hair Pass", 0); #else g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR); @@ -125,6 +189,67 @@ void DRW_hair_init(void) } } +static void drw_hair_particle_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, + ParticleHairCache *cache, + const int subdiv) +{ + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex); + DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex); + DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); + DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); +} + +static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, const int subdiv) +{ + const int strands_len = cache->strands_len; + const int final_points_len = cache->final[subdiv].strands_res * strands_len; + if (final_points_len > 0) { + GPUShader *shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); + DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); + drw_hair_particle_cache_shgrp_attach_resources(shgrp, cache, subdiv); + DRW_shgroup_vertex_buffer(shgrp, "hairPointOutputBuffer", cache->final[subdiv].proc_buf); + + const int max_strands_per_call = GPU_max_work_group_count(0); + int strands_start = 0; + while (strands_start < strands_len) { + int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); + DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); + DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); + DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); + strands_start += batch_strands_len; + } + } +} + +static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache *cache, + const int subdiv) +{ + const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; + if (final_points_len > 0) { + GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); + +#ifdef USE_TRANSFORM_FEEDBACK + DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( + tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); +#else + DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); + + ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); + pr_call->next = g_tf_calls; + pr_call->vbo = cache->final[subdiv].proc_buf; + pr_call->shgrp = tf_shgrp; + pr_call->vert_len = final_points_len; + g_tf_calls = pr_call; + DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); + DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); + DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); +#endif + + drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); + DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); + } +} + static ParticleHairCache *drw_hair_particle_cache_get( Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res) { @@ -140,32 +265,11 @@ static ParticleHairCache *drw_hair_particle_cache_get( } if (update) { - int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; - if (final_points_len > 0) { - GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM); - -#ifdef USE_TRANSFORM_FEEDBACK - DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( - tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); -#else - DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - - ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); - pr_call->next = g_tf_calls; - pr_call->vbo = cache->final[subdiv].proc_buf; - pr_call->shgrp = tf_shgrp; - pr_call->vert_len = final_points_len; - g_tf_calls = pr_call; - DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); - DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); - DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); -#endif - - DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", cache->point_tex); - DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", cache->strand_tex); - DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); - DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); - DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); + if (drw_hair_use_compute_shaders()) { + drw_hair_particle_cache_update_compute(cache, subdiv); + } + else { + drw_hair_particle_cache_update_transform_feedback(cache, subdiv); } } return cache; @@ -367,9 +471,11 @@ void DRW_hair_update(void) MEM_freeN(data); GPU_framebuffer_free(fb); #else - /* TODO(fclem): replace by compute shader. */ - /* Just render using transform feedback. */ + /* Just render the pass when using compute shaders or transform feedback. */ DRW_draw_pass(g_tf_pass); + if (drw_hair_use_compute_shaders()) { + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + } #endif } diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 84bc0327aa2..d4e22c83798 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -187,6 +187,10 @@ typedef enum { DRW_CMD_DRAW_INSTANCE = 2, DRW_CMD_DRAW_INSTANCE_RANGE = 3, DRW_CMD_DRAW_PROCEDURAL = 4, + + /* Compute Commands. */ + DRW_CMD_COMPUTE = 8, + /* Other Commands */ DRW_CMD_CLEAR = 12, DRW_CMD_DRWSTATE = 13, @@ -224,6 +228,12 @@ typedef struct DRWCommandDrawInstanceRange { uint inst_count; } DRWCommandDrawInstanceRange; +typedef struct DRWCommandCompute { + int groups_x_len; + int groups_y_len; + int groups_z_len; +} DRWCommandCompute; + typedef struct DRWCommandDrawProcedural { GPUBatch *batch; DRWResourceHandle handle; @@ -260,6 +270,7 @@ typedef union DRWCommand { DRWCommandDrawInstance instance; DRWCommandDrawInstanceRange instance_range; DRWCommandDrawProcedural procedural; + DRWCommandCompute compute; DRWCommandSetMutableState state; DRWCommandSetStencil stencil; DRWCommandSetSelectID select_id; @@ -274,6 +285,7 @@ struct DRWCallBuffer { }; /** Used by #DRWUniform.type */ +/* TODO(jbakker): rename to DRW_RESOURCE/DRWResourceType. */ typedef enum { DRW_UNIFORM_INT = 0, DRW_UNIFORM_INT_COPY, @@ -286,6 +298,7 @@ typedef enum { DRW_UNIFORM_BLOCK, DRW_UNIFORM_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, + DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, /** Per drawcall uniforms/UBO */ DRW_UNIFORM_BLOCK_OBMATS, DRW_UNIFORM_BLOCK_OBINFOS, diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 6bdc5305fed..3b852e7f8c8 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -47,6 +47,7 @@ #endif #include "GPU_buffers.h" +#include "GPU_capabilities.h" #include "GPU_material.h" #include "GPU_uniform_buffer.h" @@ -446,6 +447,19 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup, } } +void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf *vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + BLI_assert(false && "Unable to locate binding of shader storage buffer objects."); + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -700,6 +714,17 @@ static void drw_command_draw_intance_range( cmd->inst_count = count; } +static void drw_command_compute(DRWShadingGroup *shgroup, + int groups_x_len, + int groups_y_len, + int groups_z_len) +{ + DRWCommandCompute *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE); + cmd->groups_x_len = groups_x_len; + cmd->groups_y_len = groups_y_len; + cmd->groups_z_len = groups_z_len; +} + static void drw_command_draw_procedural(DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, @@ -815,6 +840,17 @@ void DRW_shgroup_call_instance_range( drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); } +void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, + int groups_x_len, + int groups_y_len, + int groups_z_len) +{ + BLI_assert(groups_x_len > 0 && groups_y_len > 0 && groups_z_len > 0); + BLI_assert(GPU_compute_shader_support()); + + drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len); +} + static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup, GPUBatch *geom, Object *ob, diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 4c8fcb0e016..f29caebeb84 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -29,6 +29,7 @@ #include "BKE_global.h" +#include "GPU_compute.h" #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_state.h" @@ -672,6 +673,9 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, ((GPUVertBuf *)uni->pvalue)); break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE: + GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location); + break; /* Legacy/Fallback support. */ case DRW_UNIFORM_BASE_INSTANCE: state->baseinst_loc = uni->location; @@ -1050,6 +1054,12 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) cmd->instance_range.inst_count, false); break; + case DRW_CMD_COMPUTE: + GPU_compute_dispatch(shgroup->shader, + cmd->compute.groups_x_len, + cmd->compute.groups_y_len, + cmd->compute.groups_z_len); + break; } } diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index 8684d82f228..02c335ddae2 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -28,6 +28,9 @@ uniform bool hairCloseTip = true; uniform vec4 hairDupliMatrix[4]; +/* Strand batch offset when used in compute shaders. */ +uniform int hairStrandOffset = 0; + /* -- Per control points -- */ uniform samplerBuffer hairPointBuffer; /* RGBA32F */ #define point_position xyz @@ -43,13 +46,37 @@ uniform usamplerBuffer hairStrandSegBuffer; /* R16UI */ /* -- Subdivision stage -- */ /** - * We use a transform feedback to preprocess the strands and add more subdivision to it. - * For the moment these are simple smooth interpolation but one could hope to see the full + * We use a transform feedback or compute shader to preprocess the strands and add more subdivision + * to it. For the moment these are simple smooth interpolation but one could hope to see the full * children particle modifiers being evaluated at this stage. * * If no more subdivision is needed, we can skip this step. */ +#ifdef GPU_VERTEX_SHADER +float hair_get_local_time() +{ + return float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1); +} + +int hair_get_id() +{ + return gl_VertexID / hairStrandsRes; +} +#endif + +#ifdef GPU_COMPUTE_SHADER +float hair_get_local_time() +{ + return float(gl_GlobalInvocationID.y) / float(hairStrandsRes - 1); +} + +int hair_get_id() +{ + return int(gl_GlobalInvocationID.x) + hairStrandOffset; +} +#endif + #ifdef HAIR_PHASE_SUBDIV int hair_get_base_id(float local_time, int strand_segments, out float interp_time) { @@ -64,9 +91,9 @@ int hair_get_base_id(float local_time, int strand_segments, out float interp_tim void hair_get_interp_attrs( out vec4 data0, out vec4 data1, out vec4 data2, out vec4 data3, out float interp_time) { - float local_time = float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1); + float local_time = hair_get_local_time(); - int hair_id = gl_VertexID / hairStrandsRes; + int hair_id = hair_get_id(); int strand_offset = int(texelFetch(hairStrandBuffer, hair_id).x); int strand_segments = int(texelFetch(hairStrandSegBuffer, hair_id).x); @@ -96,6 +123,7 @@ void hair_get_interp_attrs( */ #if !defined(HAIR_PHASE_SUBDIV) && defined(GPU_VERTEX_SHADER) + int hair_get_strand_id(void) { return gl_VertexID / (hairStrandsRes * hairThicknessRes); @@ -227,3 +255,45 @@ vec2 hair_resolve_barycentric(vec2 vert_barycentric) return vec2(1.0 - vert_barycentric.x, 0.0); } } + +/* Hair interpolation functions. */ +vec4 hair_get_weights_cardinal(float t) +{ + float t2 = t * t; + float t3 = t2 * t; +#if defined(CARDINAL) + float fc = 0.71; +#else /* defined(CATMULL_ROM) */ + float fc = 0.5; +#endif + + vec4 weights; + /* GLSL Optimized version of key_curve_position_weights() */ + float fct = t * fc; + float fct2 = t2 * fc; + float fct3 = t3 * fc; + weights.x = (fct2 * 2.0 - fct3) - fct; + weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0; + weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct; + weights.w = fct3 - fct2; + return weights; +} + +/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */ +vec4 hair_get_weights_bspline(float t) +{ + float t2 = t * t; + float t3 = t2 * t; + + vec4 weights; + /* GLSL Optimized version of key_curve_position_weights() */ + weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666); + weights.y = (0.5 * t3 - t2 + 0.66666666); + weights.w = (0.16666666 * t3); + return weights; +} + +vec4 hair_interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w) +{ + return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w; +} diff --git a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl new file mode 100644 index 00000000000..4dcde4b0245 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl @@ -0,0 +1,24 @@ + +/* + * To be compiled with common_hair_lib.glsl. + */ + +layout(local_size_x = 1, local_size_y = 1) in; +layout(std430, binding = 0) writeonly buffer hairPointOutputBuffer +{ + vec4 posTime[]; +} +out_vertbuf; + +void main(void) +{ + float interp_time; + vec4 data0, data1, data2, data3; + hair_get_interp_attrs(data0, data1, data2, data3, interp_time); + + vec4 weights = hair_get_weights_cardinal(interp_time); + vec4 result = hair_interp_data(data0, data1, data2, data3, weights); + + uint index = uint(hair_get_id() * hairStrandsRes) + gl_GlobalInvocationID.y; + out_vertbuf.posTime[index] = result; +} diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl index 3f5e3f8226f..371d43827b9 100644 --- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl +++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl @@ -3,47 +3,6 @@ out vec4 finalColor; -vec4 get_weights_cardinal(float t) -{ - float t2 = t * t; - float t3 = t2 * t; -#if defined(CARDINAL) - float fc = 0.71; -#else /* defined(CATMULL_ROM) */ - float fc = 0.5; -#endif - - vec4 weights; - /* GLSL Optimized version of key_curve_position_weights() */ - float fct = t * fc; - float fct2 = t2 * fc; - float fct3 = t3 * fc; - weights.x = (fct2 * 2.0 - fct3) - fct; - weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0; - weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct; - weights.w = fct3 - fct2; - return weights; -} - -/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */ -vec4 get_weights_bspline(float t) -{ - float t2 = t * t; - float t3 = t2 * t; - - vec4 weights; - /* GLSL Optimized version of key_curve_position_weights() */ - weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666); - weights.y = (0.5 * t3 - t2 + 0.66666666); - weights.w = (0.16666666 * t3); - return weights; -} - -vec4 interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w) -{ - return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w; -} - #ifdef TF_WORKAROUND uniform int targetWidth; uniform int targetHeight; @@ -56,8 +15,8 @@ void main(void) vec4 data0, data1, data2, data3; hair_get_interp_attrs(data0, data1, data2, data3, interp_time); - vec4 weights = get_weights_cardinal(interp_time); - finalColor = interp_data(data0, data1, data2, data3, weights); + vec4 weights = hair_get_weights_cardinal(interp_time); + finalColor = hair_interp_data(data0, data1, data2, data3, weights); #ifdef TF_WORKAROUND int id = gl_VertexID - idOffset; diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index 45c656b49be..0c054d4f264 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -37,6 +37,8 @@ int GPU_max_textures(void); int GPU_max_textures_vert(void); int GPU_max_textures_geom(void); int GPU_max_textures_frag(void); +int GPU_max_work_group_count(int index); +int GPU_max_work_group_size(int index); int GPU_max_uniforms_vert(void); int GPU_max_uniforms_frag(void); int GPU_max_batch_indices(void); diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index bedc9ad3092..c6e9dc210cb 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -82,6 +82,16 @@ int GPU_max_textures(void) return GCaps.max_textures; } +int GPU_max_work_group_count(int index) +{ + return GCaps.max_work_group_count[index]; +} + +int GPU_max_work_group_size(int index) +{ + return GCaps.max_work_group_size[index]; +} + int GPU_max_uniforms_vert(void) { return GCaps.max_uniforms_vert; diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index ee7ef1e69e6..95cf7fd335d 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -41,6 +41,8 @@ struct GPUCapabilities { int max_textures_vert = 0; int max_textures_geom = 0; int max_textures_frag = 0; + int max_work_group_count[3] = {0, 0, 0}; + int max_work_group_size[3] = {0, 0, 0}; int max_uniforms_vert = 0; int max_uniforms_frag = 0; int max_batch_indices = 0; diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index fb03a2c2d2a..d85f9f7684d 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -438,6 +438,14 @@ 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; + 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]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &GCaps.max_work_group_count[2]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &GCaps.max_work_group_size[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &GCaps.max_work_group_size[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]); + } GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object; /* GL specific capabilities. */ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size); -- cgit v1.2.3 From 3bee77bb7c234832b9ca00222e1e90a24f6437b0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 28 May 2021 16:05:46 +1000 Subject: Cleanup: use static set syntax --- doc/blender_file_format/BlendFileReader.py | 4 ++-- release/scripts/startup/bl_ui/space_toolsystem_toolbar.py | 2 +- source/blender/python/rna_dump.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/blender_file_format/BlendFileReader.py b/doc/blender_file_format/BlendFileReader.py index d4aed722578..a2f214bc4fc 100644 --- a/doc/blender_file_format/BlendFileReader.py +++ b/doc/blender_file_format/BlendFileReader.py @@ -85,7 +85,7 @@ def openBlendFile(filename): ''' handle = open(filename, 'rb') magic = ReadString(handle, 7) - if magic in ("BLENDER", "BULLETf"): + if magic in {"BLENDER", "BULLETf"}: log.debug("normal blendfile detected") handle.seek(0, os.SEEK_SET) return handle @@ -137,7 +137,7 @@ class BlendFile: fileblock = BlendFileBlock(handle, self) found_dna_block = False while not found_dna_block: - if fileblock.Header.Code in ("DNA1", "SDNA"): + if fileblock.Header.Code in {"DNA1", "SDNA"}: self.Catalog = DNACatalog(self.Header, handle) found_dna_block = True else: diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 8c683c3f205..ce1c401b14b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -164,7 +164,7 @@ class _defs_annotate: gpl = context.active_annotation_layer if gpl is not None: layout.label(text="Annotation:") - if context.space_data.type in ('VIEW_3D', 'SEQUENCE_EDITOR'): + if context.space_data.type in {'VIEW_3D', 'SEQUENCE_EDITOR'}: if region_type == 'TOOL_HEADER': sub = layout.split(align=True, factor=0.5) sub.ui_units_x = 6.5 diff --git a/source/blender/python/rna_dump.py b/source/blender/python/rna_dump.py index 7d469d20110..bd45a36e8a4 100644 --- a/source/blender/python/rna_dump.py +++ b/source/blender/python/rna_dump.py @@ -62,7 +62,7 @@ def seek(r, txt, recurs): # print(dir(r)) # basic types - if type_r in (float, int, bool, type(None)): + if type_r in {float, int, bool, type(None)}: if PRINT_DATA: print(txt + ' -> ' + str(r)) return -- cgit v1.2.3 From 3ec57ce6c7fcd3aa59c9b60139321c11f73fb7f7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 28 May 2021 15:44:32 +1000 Subject: Tests: add utility to generate interactive user actions A utility that supports passing in actions as command line arguments for writing reproducible interactions, benchmarking, profiling and testing. Unlike regular scripts this is able to control model operators usefully. Typical ways of controlling Blender using this utility are via operator id's, menu search and explicit events. Others methods can be added as needed. See the doc-string for example usage. --- tests/python/bl_run_operators_event_simulate.py | 577 ++++++++++++++++++++++++ 1 file changed, 577 insertions(+) create mode 100644 tests/python/bl_run_operators_event_simulate.py diff --git a/tests/python/bl_run_operators_event_simulate.py b/tests/python/bl_run_operators_event_simulate.py new file mode 100644 index 00000000000..e536b9da2b1 --- /dev/null +++ b/tests/python/bl_run_operators_event_simulate.py @@ -0,0 +1,577 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +r""" +Overview +======== + +This is a utility to generate events from the command line, +so reproducible test cases can be written without having to create a custom script each time. + +The key differentiating feature for this utility as is that it's able to control modal operators. + +Possible use cases for this script include: + +- Creating reproducible user interactions for the purpose of benchmarking and profiling. + + Note that curse-motion actions report the update time between events + which can be helpful when measuring optimizations. + +- As a convenient way to replay interactive actions that reproduce a bug. + +- For writing tests (although some extra functionality may be necessary in this case). + + +Actions +======= + +You will notice most of the functionality is supported using the actions command line argument, +this is a kind of mini-language to drive Blender. + +While the current set of commands is fairly limited more can be added as needed. + +To see a list of actions as well as their arguments run: + + ./blender.bin --python tests/python/bl_run_operators_event_simulate.py -- --help + + +Examples +======== + +Rotate in edit-mode examples: + + ./blender.bin \ + --factory-startup \ + --enable-event-simulate \ + --python tests/python/bl_run_operators_event_simulate.py \ + -- \ + --actions \ + 'area_maximize(ui_type="VIEW_3D")' \ + 'operator("object.mode_set", mode="EDIT")' \ + 'operator("mesh.select_all", action="SELECT")' \ + 'operator("mesh.subdivide", number_cuts=5)' \ + 'operator("transform.rotate")' \ + 'cursor_motion(path="CIRCLE", radius=300, steps=100, repeat=2)' + +Sculpt stroke: + + ./blender.bin \ + --factory-startup \ + --enable-event-simulate \ + --python tests/python/bl_run_operators_event_simulate.py \ + -- \ + --actions \ + 'area_maximize(ui_type="VIEW_3D")' \ + 'event(type="FIVE", value="TAP", ctrl=True)' \ + 'menu("Visual Geometry to Mesh")' \ + 'menu("Frame Selected")' \ + 'menu("Toggle Sculpt Mode")' \ + 'event(type="WHEELDOWNMOUSE", value="TAP", repeat=2)' \ + 'event(type="LEFTMOUSE", value="PRESS")' \ + 'cursor_motion(path="CIRCLE", radius=300, steps=100, repeat=5)' \ + 'event(type="LEFTMOUSE", value="RELEASE")' \ + + +Implementation +============== + +While most of the operations listed above can be executed in Python directly, +either the event loop won't be handled between actions (the case for typical Python script), +or the context for executing the actions is not properly set (the case for timers). + +This utility executes actions as if the user initiated them from a key shortcut. +""" + + +import os +import sys +import argparse +from argparse import ArgumentTypeError + +import bpy + + +# ----------------------------------------------------------------------------- +# Constants + +EVENT_TYPES = tuple(bpy.types.Event.bl_rna.properties["type"].enum_items.keys()) +EVENT_VALUES = tuple(bpy.types.Event.bl_rna.properties["value"].enum_items.keys()) +# `TAP` is just convenience for (`PRESS`, `RELEASE`). +EVENT_VALUES_EXTRA = EVENT_VALUES + ('TAP',) + + +# ----------------------------------------------------------------------------- +# Globals + +# Assign a global since this script is not going to be loading new files (which would free the window). +win = bpy.context.window_manager.windows[0] + + +# ----------------------------------------------------------------------------- +# Utilities + +def find_main_area(ui_type=None): + """ + Find the largest area from the current screen. + """ + area_best = None + size_best = -1 + for area in win.screen.areas: + if ui_type is not None: + if ui_type != area.ui_type: + continue + + size = area.width * area.height + if size > size_best: + size_best = size + area_best = area + return area_best + + +def gen_events_type_text(text): + """ + Generate events to type in `text`. + """ + for ch in text: + kw_extra = {} + # The event type in this case is ignored as only the unicode value is used for text input. + type = 'SPACE' + if ch == '\t': + type = 'TAB' + elif ch == '\n': + type = 'RET' + else: + kw_extra["unicode"] = ch + + yield dict(type=type, value='PRESS', **kw_extra) + kw_extra.pop("unicode", None) + yield dict(type=type, value='RELEASE', **kw_extra) + + +# ----------------------------------------------------------------------------- +# Simulate Events + +def mouse_location_get(): + return ( + run_event_simulate.last_event["x"], + run_event_simulate.last_event["y"], + ) + + +def run_event_simulate(*, event_iter): + """ + Pass events from event_iter into Blender. + """ + last_event = run_event_simulate.last_event + + def event_step(): + win = bpy.context.window_manager.windows[0] + + val = next(event_step.run_events, Ellipsis) + if val is Ellipsis: + bpy.app.use_event_simulate = False + print("Finished simulation") + + sys.exit(0) + return None + + # Run event simulation. + for attr in ("x", "y"): + if attr in val: + last_event[attr] = val[attr] + else: + val[attr] = last_event[attr] + + # Fake event value, since press, release is so common. + if val.get("value") == 'TAP': + del val["value"] + win.event_simulate(**val, value='PRESS') + # Needed if new files are loaded. + # win = bpy.context.window_manager.windows[0] + win.event_simulate(**val, value='RELEASE') + else: + # print("val", val) + win.event_simulate(**val) + return 0.0 + + event_step.run_events = iter(event_iter) + + bpy.app.timers.register(event_step, first_interval=0.0, persistent=True) + + +run_event_simulate.last_event = dict( + x=win.width // 2, + y=win.height // 2, +) + + +# ----------------------------------------------------------------------------- +# Action Implementations + +# Static methods from this class are automatically exposed as actions and included in the help text. +class action_handlers: + + @staticmethod + def area_maximize(*, ui_type=None, only_validate=False): + """ + ui_type: + Select the area type (typically 'VIEW_3D'). + Note that this area type needs to exist in the current screen. + """ + if not ((ui_type is None) or (isinstance(ui_type, str))): + raise ArgumentTypeError("'type' argument %r not None or a string type") + + if only_validate: + return + + area = find_main_area(ui_type=ui_type) + if area is None: + raise ArgumentTypeError("Area with ui_type=%r not found" % ui_type) + + x = area.y + (area.width // 2) + y = area.x + (area.height // 2) + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x, y=y) + yield dict(type='SPACE', value='TAP', ctrl=True, alt=True) + + x = win.width // 2 + y = win.height // 2 + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x, y=y) + + @staticmethod + def menu(text, *, only_validate=False): + """ + text: Menu item to search for and execute. + """ + if not isinstance(text, str): + raise ArgumentTypeError("'text' argument not a string") + + if only_validate: + return + + yield dict(type='F3', value='TAP') + yield from gen_events_type_text(text) + yield dict(type='RET', value='TAP') + + @staticmethod + def event(*, value, type, ctrl=False, alt=False, shift=False, repeat=1, only_validate=False): + """ + value: The event, typically key, e.g. 'ESC', 'RET', 'SPACE', 'A'. + type: The event type, valid values include: 'PRESS', 'RELEASE', 'TAP'. + ctrl: Control modifier. + alt: Alt modifier. + shift: Shift modifier. + """ + valid_items = EVENT_VALUES_EXTRA + if value not in valid_items: + raise ArgumentTypeError("'value' argument %r not in %r" % (value, valid_items)) + valid_items = EVENT_TYPES + if type not in valid_items: + raise ArgumentTypeError("'type' argument %r not in %r" % (value, valid_items)) + valid_items = range(1, sys.maxsize) + if repeat not in valid_items: + raise ArgumentTypeError("'repeat' argument %r not in %r" % (repeat, valid_items)) + del valid_items + + if only_validate: + return + + for _ in range(repeat): + yield dict(type=type, ctrl=ctrl, alt=alt, shift=shift, value=value) + + @staticmethod + def cursor_motion(*, path, steps, radius=100, repeat=1, only_validate=False): + """ + path: The path type to use in ('CIRCLE'). + steps: The number of events to generate. + radius: The radius in pixels. + repeat: Number of times to repeat the cursor rotation. + """ + + import time + from math import sin, cos, pi + + valid_items = range(1, sys.maxsize) + if steps not in valid_items: + raise ArgumentTypeError("'steps' argument %r not in %r" % (steps, valid_items)) + + valid_items = range(1, sys.maxsize) + if radius not in valid_items: + raise ArgumentTypeError("'radius' argument %r not in %r" % (steps, valid_items)) + + valid_items = ('CIRCLE',) + if path not in valid_items: + raise ArgumentTypeError("'path' argument %r not in %r" % (path, valid_items)) + + valid_items = range(1, sys.maxsize) + if repeat not in valid_items: + raise ArgumentTypeError("'repeat' argument %r not in %r" % (repeat, valid_items)) + del valid_items + + if only_validate: + return + + x_init, y_init = mouse_location_get() + + y_init_ofs = y_init + radius + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init_ofs) + + print("\n" "Times for: %s" % os.path.basename(bpy.data.filepath)) + + t = time.time() + step_total = 0 + + if path == 'CIRCLE': + for _ in range(repeat): + for i in range(1, steps + 1): + phi = (i / steps) * 2.0 * pi + x_ofs = -radius * sin(phi) + y_ofs = +radius * cos(phi) + step_total += 1 + yield dict( + type='MOUSEMOVE', + value='NOTHING', + x=int(x_init + x_ofs), + y=int(y_init + y_ofs), + ) + + delta = time.time() - t + delta_step = delta / step_total + print( + "Average:", + ("%.6f FPS" % (1 / delta_step)).rjust(10), + ) + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init) + + @staticmethod + def operator(idname, *, only_validate=False, **kw): + """ + idname: The operator identifier (positional argument only). + kw: Passed to the operator. + """ + + # Create a temporary key binding to call the operator. + wm = bpy.context.window_manager + keyconf = wm.keyconfigs.user + + keymap_id = "Screen" + key_to_map = 'F24' + + if only_validate: + op_mod, op_submod = idname.partition(".")[0::2] + op = getattr(getattr(bpy.ops, op_mod), op_submod) + try: + # The poll result doesn't matter we only want to know if the operator exists or not. + op.poll() + except AttributeError: + raise ArgumentTypeError("Operator %r does not exist" % (idname)) + + keymap = keyconf.keymaps[keymap_id] + kmi = keymap.keymap_items.new(idname=idname, type=key_to_map, value='PRESS') + kmi.idname = idname + props = kmi.properties + for key, value in kw.items(): + if not hasattr(props, key): + raise ArgumentTypeError("Operator %r does not have a %r property" % (idname, key)) + + try: + setattr(props, key, value) + except Exception as ex: + raise ArgumentTypeError("Operator %r assign %r property with error %s" % (idname, key, str(ex))) + + keymap.keymap_items.remove(kmi) + return + + keymap = keyconf.keymaps[keymap_id] + kmi = keymap.keymap_items.new(idname=idname, type=key_to_map, value='PRESS') + kmi.idname = idname + props = kmi.properties + for key, value in kw.items(): + setattr(props, key, value) + + yield dict(type=key_to_map, value='TAP') + + keymap = keyconf.keymaps[keymap_id] + kmi = keymap.keymap_items[-1] + keymap.keymap_items.remove(kmi) + + +ACTION_DIR = tuple([ + key for key in sorted(action_handlers.__dict__.keys()) + if not key.startswith("_") +]) + + +def handle_action(op, args, kwargs, only_validate=False): + fn = getattr(action_handlers, op, None) + if fn is None: + raise ArgumentTypeError("Action %r is not found in %r" % (op, ACTION_DIR)) + yield from fn(*args, **kwargs, only_validate=only_validate) + + +# ----------------------------------------------------------------------------- +# Argument Parsing + + +class BlenderAction(argparse.Action): + """ + This class is used to extract positional & keyword arguments from + a string, validate them, and return the (action, positional_args, keyword_args). + + All of this happens during argument parsing so any errors in the actions + show useful error messages instead of failing to execute part way through. + """ + + @staticmethod + def _parse_value(value, index): + """ + Convert: + "value(1, 2, a=1, b='', c=None)" + To: + ("value", (1, 2), {"a": 1, "b": "", "c": None}) + """ + split = value.find("(") + if split == -1: + op = value + args = None + kwargs = None + else: + op = value[:split] + namespace = {op: lambda *args, **kwargs: (args, kwargs)} + expr = value + try: + args, kwargs = eval(expr, namespace, namespace) + except Exception as ex: + raise ArgumentTypeError("Unable to parse \"%s\" at index %d, error: %s" % (expr, index, str(ex))) + + # Creating a list is necessary since this is a generator. + try: + dummy_result = list(handle_action(op, args, kwargs, only_validate=True)) + except ArgumentTypeError as ex: + raise ArgumentTypeError("Invalid 'action' arguments \"%s\" at index %d, %s" % (value, index, str(ex))) + # Validation should never yield any events. + assert(not dummy_result) + + return (op, args, kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + setattr( + namespace, + self.dest, [ + self._parse_value(value, index) + for index, value in enumerate(values) + ], + ) + + +def argparse_create(): + import inspect + import textwrap + + # When --help or no args are given, print this help + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawTextHelpFormatter, + ) + + # Collect doc-strings from static methods in `actions`. + actions_docstring = [] + for action_key in ACTION_DIR: + action = getattr(action_handlers, action_key) + args = str(inspect.signature(action)) + args = "(" + args[1:].removeprefix("*, ") + args = args.replace(", *, ", ", ") # Needed in case the are positional arguments. + args = args.replace(", only_validate=False", "") + + actions_docstring.append("- %s%s\n" % (action_key, args)) + docs = textwrap.dedent((action.__doc__ or "").lstrip("\n").rstrip()) + "\n\n" + + actions_docstring.append(textwrap.indent(docs, " ")) + + parser.add_argument( + "--actions", + dest="actions", + metavar='ACTIONS', type=str, + help=( + "\n" "Arguments must use one of the following prefix:\n" + "\n" + "".join(actions_docstring) + ), + nargs='+', + required=True, + action=BlenderAction, + ) + + return parser + + +# ----------------------------------------------------------------------------- +# Default Startup + + +def setup_default_preferences(prefs): + """ + Set preferences useful for automation. + """ + prefs.view.show_splash = False + prefs.view.smooth_view = 0 + prefs.view.use_save_prompt = False + prefs.view.show_developer_ui = True + prefs.filepaths.use_auto_save_temporary_files = False + + +# ----------------------------------------------------------------------------- +# Main Function + + +def main_event_iter(*, action_list): + """ + Yield all events from action handlers. + """ + area = find_main_area() + + x_init = area.y + (area.width // 2) + y_init = area.x + (area.height // 2) + + yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init) + + for (op, args, kwargs) in action_list: + yield from handle_action(op, args, kwargs) + + +def main(): + from sys import argv + argv = argv[argv.index("--") + 1:] if "--" in argv else [] + + try: + args = argparse_create().parse_args(argv) + except ArgumentTypeError as ex: + print(ex) + sys.exit(1) + + setup_default_preferences(bpy.context.preferences) + + run_event_simulate(event_iter=main_event_iter(action_list=args.actions)) + + +if __name__ == "__main__": + main() -- cgit v1.2.3 From 5baf1dddd54eb320799632e69cef5b711250cba3 Mon Sep 17 00:00:00 2001 From: James Monteath Date: Fri, 28 May 2021 10:16:39 +0200 Subject: Buildbot related files have been moved to own repository --- build_files/buildbot/buildbot_utils.py | 127 ----- .../codesign/absolute_and_relative_filename.py | 81 --- .../buildbot/codesign/archive_with_indicator.py | 245 --------- build_files/buildbot/codesign/base_code_signer.py | 501 ------------------- build_files/buildbot/codesign/config_builder.py | 62 --- build_files/buildbot/codesign/config_common.py | 36 -- .../buildbot/codesign/config_server_template.py | 101 ---- build_files/buildbot/codesign/exception.py | 26 - build_files/buildbot/codesign/linux_code_signer.py | 72 --- build_files/buildbot/codesign/macos_code_signer.py | 456 ----------------- .../buildbot/codesign/simple_code_signer.py | 52 -- build_files/buildbot/codesign/util.py | 54 -- .../buildbot/codesign/windows_code_signer.py | 117 ----- build_files/buildbot/codesign_server_linux.py | 37 -- build_files/buildbot/codesign_server_macos.py | 41 -- build_files/buildbot/codesign_server_windows.bat | 11 - build_files/buildbot/codesign_server_windows.py | 54 -- build_files/buildbot/worker_bundle_dmg.py | 551 --------------------- build_files/buildbot/worker_codesign.cmake | 44 -- build_files/buildbot/worker_codesign.py | 74 --- build_files/buildbot/worker_compile.py | 135 ----- build_files/buildbot/worker_pack.py | 208 -------- build_files/buildbot/worker_test.py | 42 -- build_files/buildbot/worker_update.py | 31 -- 24 files changed, 3158 deletions(-) delete mode 100644 build_files/buildbot/buildbot_utils.py delete mode 100644 build_files/buildbot/codesign/absolute_and_relative_filename.py delete mode 100644 build_files/buildbot/codesign/archive_with_indicator.py delete mode 100644 build_files/buildbot/codesign/base_code_signer.py delete mode 100644 build_files/buildbot/codesign/config_builder.py delete mode 100644 build_files/buildbot/codesign/config_common.py delete mode 100644 build_files/buildbot/codesign/config_server_template.py delete mode 100644 build_files/buildbot/codesign/exception.py delete mode 100644 build_files/buildbot/codesign/linux_code_signer.py delete mode 100644 build_files/buildbot/codesign/macos_code_signer.py delete mode 100644 build_files/buildbot/codesign/simple_code_signer.py delete mode 100644 build_files/buildbot/codesign/util.py delete mode 100644 build_files/buildbot/codesign/windows_code_signer.py delete mode 100755 build_files/buildbot/codesign_server_linux.py delete mode 100755 build_files/buildbot/codesign_server_macos.py delete mode 100644 build_files/buildbot/codesign_server_windows.bat delete mode 100755 build_files/buildbot/codesign_server_windows.py delete mode 100755 build_files/buildbot/worker_bundle_dmg.py delete mode 100644 build_files/buildbot/worker_codesign.cmake delete mode 100755 build_files/buildbot/worker_codesign.py delete mode 100644 build_files/buildbot/worker_compile.py delete mode 100644 build_files/buildbot/worker_pack.py delete mode 100644 build_files/buildbot/worker_test.py delete mode 100644 build_files/buildbot/worker_update.py diff --git a/build_files/buildbot/buildbot_utils.py b/build_files/buildbot/buildbot_utils.py deleted file mode 100644 index 7e9858d9268..00000000000 --- a/build_files/buildbot/buildbot_utils.py +++ /dev/null @@ -1,127 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -import argparse -import os -import re -import subprocess -import sys - - -def is_tool(name): - """Check whether `name` is on PATH and marked as executable.""" - - # from whichcraft import which - from shutil import which - - return which(name) is not None - - -class Builder: - def __init__(self, name, branch, codesign): - self.name = name - self.branch = branch - self.is_release_branch = re.match("^blender-v(.*)-release$", branch) is not None - self.codesign = codesign - - # Buildbot runs from build/ directory - self.blender_dir = os.path.abspath(os.path.join('..', 'blender.git')) - self.build_dir = os.path.abspath(os.path.join('..', 'build')) - self.install_dir = os.path.abspath(os.path.join('..', 'install')) - self.upload_dir = os.path.abspath(os.path.join('..', 'install')) - - # Detect platform - if name.startswith('mac'): - self.platform = 'mac' - self.command_prefix = [] - elif name.startswith('linux'): - self.platform = 'linux' - if is_tool('scl'): - self.command_prefix = ['scl', 'enable', 'devtoolset-9', '--'] - else: - self.command_prefix = [] - elif name.startswith('win'): - self.platform = 'win' - self.command_prefix = [] - else: - raise ValueError('Unkonw platform for builder ' + self.platform) - - # Always 64 bit now - self.bits = 64 - - -def create_builder_from_arguments(): - parser = argparse.ArgumentParser() - parser.add_argument('builder_name') - parser.add_argument('branch', default='master', nargs='?') - parser.add_argument("--codesign", action="store_true") - args = parser.parse_args() - return Builder(args.builder_name, args.branch, args.codesign) - - -class VersionInfo: - def __init__(self, builder): - # Get version information - buildinfo_h = os.path.join(builder.build_dir, "source", "creator", "buildinfo.h") - blender_h = os.path.join(builder.blender_dir, "source", "blender", "blenkernel", "BKE_blender_version.h") - - version_number = int(self._parse_header_file(blender_h, 'BLENDER_VERSION')) - version_number_patch = int(self._parse_header_file(blender_h, 'BLENDER_VERSION_PATCH')) - version_numbers = (version_number // 100, version_number % 100, version_number_patch) - self.short_version = "%d.%d" % (version_numbers[0], version_numbers[1]) - self.version = "%d.%d.%d" % version_numbers - self.version_cycle = self._parse_header_file(blender_h, 'BLENDER_VERSION_CYCLE') - self.hash = self._parse_header_file(buildinfo_h, 'BUILD_HASH')[1:-1] - - if self.version_cycle == "release": - # Final release - self.full_version = self.version - self.is_development_build = False - elif self.version_cycle == "rc": - # Release candidate - self.full_version = self.version + self.version_cycle - self.is_development_build = False - else: - # Development build - self.full_version = self.version + '-' + self.hash - self.is_development_build = True - - def _parse_header_file(self, filename, define): - import re - regex = re.compile(r"^#\s*define\s+%s\s+(.*)" % define) - with open(filename, "r") as file: - for l in file: - match = regex.match(l) - if match: - return match.group(1) - return None - - -def call(cmd, env=None, exit_on_error=True): - print(' '.join(cmd)) - - # Flush to ensure correct order output on Windows. - sys.stdout.flush() - sys.stderr.flush() - - retcode = subprocess.call(cmd, env=env) - if exit_on_error and retcode != 0: - sys.exit(retcode) - return retcode diff --git a/build_files/buildbot/codesign/absolute_and_relative_filename.py b/build_files/buildbot/codesign/absolute_and_relative_filename.py deleted file mode 100644 index cb42710e785..00000000000 --- a/build_files/buildbot/codesign/absolute_and_relative_filename.py +++ /dev/null @@ -1,81 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -from dataclasses import dataclass -from pathlib import Path -from typing import List - - -@dataclass -class AbsoluteAndRelativeFileName: - """ - Helper class which keeps track of absolute file path for a direct access and - corresponding relative path against given base. - - The relative part is used to construct a file name within an archive which - contains files which are to be signed or which has been signed already - (depending on whether the archive is addressed to signing server or back - to the buildbot worker). - """ - - # Base directory which is where relative_filepath is relative to. - base_dir: Path - - # Full absolute path of the corresponding file. - absolute_filepath: Path - - # Derived from full file path, contains part of the path which is relative - # to a desired base path. - relative_filepath: Path - - def __init__(self, base_dir: Path, filepath: Path): - self.base_dir = base_dir - self.absolute_filepath = filepath.resolve() - self.relative_filepath = self.absolute_filepath.relative_to( - self.base_dir) - - @classmethod - def from_path(cls, path: Path) -> 'AbsoluteAndRelativeFileName': - assert path.is_absolute() - assert path.is_file() - - base_dir = path.parent - return AbsoluteAndRelativeFileName(base_dir, path) - - @classmethod - def recursively_from_directory(cls, base_dir: Path) \ - -> List['AbsoluteAndRelativeFileName']: - """ - Create list of AbsoluteAndRelativeFileName for all the files in the - given directory. - - NOTE: Result will be pointing to a resolved paths. - """ - assert base_dir.is_absolute() - assert base_dir.is_dir() - - base_dir = base_dir.resolve() - - result = [] - for filename in base_dir.glob('**/*'): - if not filename.is_file(): - continue - result.append(AbsoluteAndRelativeFileName(base_dir, filename)) - return result diff --git a/build_files/buildbot/codesign/archive_with_indicator.py b/build_files/buildbot/codesign/archive_with_indicator.py deleted file mode 100644 index aebf5a15417..00000000000 --- a/build_files/buildbot/codesign/archive_with_indicator.py +++ /dev/null @@ -1,245 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -import dataclasses -import json -import os - -from pathlib import Path -from typing import Optional - -import codesign.util as util - - -class ArchiveStateError(Exception): - message: str - - def __init__(self, message): - self.message = message - super().__init__(self.message) - - -@dataclasses.dataclass -class ArchiveState: - """ - Additional information (state) of the archive - - Includes information like expected file size of the archive file in the case - the archive file is expected to be successfully created. - - If the archive can not be created, this state will contain error message - indicating details of error. - """ - - # Size in bytes of the corresponding archive. - file_size: Optional[int] = None - - # Non-empty value indicates that error has happenned. - error_message: str = '' - - def has_error(self) -> bool: - """ - Check whether the archive is at error state - """ - - return self.error_message - - def serialize_to_string(self) -> str: - payload = dataclasses.asdict(self) - return json.dumps(payload, sort_keys=True, indent=4) - - def serialize_to_file(self, filepath: Path) -> None: - string = self.serialize_to_string() - filepath.write_text(string) - - @classmethod - def deserialize_from_string(cls, string: str) -> 'ArchiveState': - try: - object_as_dict = json.loads(string) - except json.decoder.JSONDecodeError: - raise ArchiveStateError('Error parsing JSON') - - return cls(**object_as_dict) - - @classmethod - def deserialize_from_file(cls, filepath: Path): - string = filepath.read_text() - return cls.deserialize_from_string(string) - - -class ArchiveWithIndicator: - """ - The idea of this class is to wrap around logic which takes care of keeping - track of a name of an archive and synchronization routines between buildbot - worker and signing server. - - The synchronization is done based on creating a special file after the - archive file is knowingly ready for access. - """ - - # Base directory where the archive is stored (basically, a basename() of - # the absolute archive file name). - # - # For example, 'X:\\TEMP\\'. - base_dir: Path - - # Absolute file name of the archive. - # - # For example, 'X:\\TEMP\\FOO.ZIP'. - archive_filepath: Path - - # Absolute name of a file which acts as an indication of the fact that the - # archive is ready and is available for access. - # - # This is how synchronization between buildbot worker and signing server is - # done: - # - First, the archive is created under archive_filepath name. - # - Second, the indication file is created under ready_indicator_filepath - # name. - # - Third, the colleague of whoever created the indicator name watches for - # the indication file to appear, and once it's there it access the - # archive. - ready_indicator_filepath: Path - - def __init__( - self, base_dir: Path, archive_name: str, ready_indicator_name: str): - """ - Construct the object from given base directory and name of the archive - file: - ArchiveWithIndicator(Path('X:\\TEMP'), 'FOO.ZIP', 'INPUT_READY') - """ - - self.base_dir = base_dir - self.archive_filepath = self.base_dir / archive_name - self.ready_indicator_filepath = self.base_dir / ready_indicator_name - - def is_ready_unsafe(self) -> bool: - """ - Check whether the archive is ready for access. - - No guarding about possible network failres is done here. - """ - if not self.ready_indicator_filepath.exists(): - return False - - try: - archive_state = ArchiveState.deserialize_from_file( - self.ready_indicator_filepath) - except ArchiveStateError as error: - print(f'Error deserializing archive state: {error.message}') - return False - - if archive_state.has_error(): - # If the error did happen during codesign procedure there will be no - # corresponding archive file. - # The caller code will deal with the error check further. - return True - - # Sometimes on macOS indicator file appears prior to the actual archive - # despite the order of creation and os.sync() used in tag_ready(). - # So consider archive not ready if there is an indicator without an - # actual archive. - if not self.archive_filepath.exists(): - print('Found indicator without actual archive, waiting for archive ' - f'({self.archive_filepath}) to appear.') - return False - - # Wait for until archive is fully stored. - actual_archive_size = self.archive_filepath.stat().st_size - if actual_archive_size != archive_state.file_size: - print('Partial/invalid archive size (expected ' - f'{archive_state.file_size} got {actual_archive_size})') - return False - - return True - - def is_ready(self) -> bool: - """ - Check whether the archive is ready for access. - - Will tolerate possible network failures: if there is a network failure - or if there is still no proper permission on a file False is returned. - """ - - # There are some intermitten problem happening at a random which is - # translates to "OSError : [WinError 59] An unexpected network error occurred". - # Some reports suggests it might be due to lack of permissions to the file, - # which might be applicable in our case since it's possible that file is - # initially created with non-accessible permissions and gets chmod-ed - # after initial creation. - try: - return self.is_ready_unsafe() - except OSError as e: - print(f'Exception checking archive: {e}') - return False - - def tag_ready(self, error_message='') -> None: - """ - Tag the archive as ready by creating the corresponding indication file. - - NOTE: It is expected that the archive was never tagged as ready before - and that there are no subsequent tags of the same archive. - If it is violated, an assert will fail. - """ - assert not self.is_ready() - - # Try the best to make sure everything is synced to the file system, - # to avoid any possibility of stamp appearing on a network share prior to - # an actual file. - if util.get_current_platform() != util.Platform.WINDOWS: - os.sync() - - archive_size = -1 - if self.archive_filepath.exists(): - archive_size = self.archive_filepath.stat().st_size - - archive_info = ArchiveState( - file_size=archive_size, error_message=error_message) - - self.ready_indicator_filepath.write_text( - archive_info.serialize_to_string()) - - def get_state(self) -> ArchiveState: - """ - Get state object for this archive - - The state is read from the corresponding state file. - """ - - try: - return ArchiveState.deserialize_from_file(self.ready_indicator_filepath) - except ArchiveStateError as error: - return ArchiveState(error_message=f'Error in information format: {error}') - - def clean(self) -> None: - """ - Remove both archive and the ready indication file. - """ - util.ensure_file_does_not_exist_or_die(self.ready_indicator_filepath) - util.ensure_file_does_not_exist_or_die(self.archive_filepath) - - def is_fully_absent(self) -> bool: - """ - Check whether both archive and its ready indicator are absent. - Is used for a sanity check during code signing process by both - buildbot worker and signing server. - """ - return (not self.archive_filepath.exists() and - not self.ready_indicator_filepath.exists()) diff --git a/build_files/buildbot/codesign/base_code_signer.py b/build_files/buildbot/codesign/base_code_signer.py deleted file mode 100644 index f045e9c8242..00000000000 --- a/build_files/buildbot/codesign/base_code_signer.py +++ /dev/null @@ -1,501 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# Signing process overview. -# -# From buildbot worker side: -# - Files which needs to be signed are collected from either a directory to -# sign all signable files in there, or by filename of a single file to sign. -# - Those files gets packed into an archive and stored in a location location -# which is watched by the signing server. -# - A marker READY file is created which indicates the archive is ready for -# access. -# - Wait for the server to provide an archive with signed files. -# This is done by watching for the READY file which corresponds to an archive -# coming from the signing server. -# - Unpack the signed signed files from the archives and replace original ones. -# -# From code sign server: -# - Watch special location for a READY file which indicates the there is an -# archive with files which are to be signed. -# - Unpack the archive to a temporary location. -# - Run codesign tool and make sure all the files are signed. -# - Pack the signed files and store them in a location which is watched by -# the buildbot worker. -# - Create a READY file which indicates that the archive with signed files is -# ready. - -import abc -import logging -import shutil -import subprocess -import time -import tarfile -import uuid - -from pathlib import Path -from tempfile import TemporaryDirectory -from typing import Iterable, List - -import codesign.util as util - -from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName -from codesign.archive_with_indicator import ArchiveWithIndicator -from codesign.exception import CodeSignException - - -logger = logging.getLogger(__name__) -logger_builder = logger.getChild('builder') -logger_server = logger.getChild('server') - - -def pack_files(files: Iterable[AbsoluteAndRelativeFileName], - archive_filepath: Path) -> None: - """ - Create tar archive from given files for the signing pipeline. - Is used by buildbot worker to create an archive of files which are to be - signed, and by signing server to send signed files back to the worker. - """ - with tarfile.TarFile.open(archive_filepath, 'w') as tar_file_handle: - for file_info in files: - tar_file_handle.add(file_info.absolute_filepath, - arcname=file_info.relative_filepath) - - -def extract_files(archive_filepath: Path, - extraction_dir: Path) -> None: - """ - Extract all files form the given archive into the given direcotry. - """ - - # TODO(sergey): Verify files in the archive have relative path. - - with tarfile.TarFile.open(archive_filepath, mode='r') as tar_file_handle: - tar_file_handle.extractall(path=extraction_dir) - - -class BaseCodeSigner(metaclass=abc.ABCMeta): - """ - Base class for a platform-specific signer of binaries. - - Contains all the logic shared across platform-specific implementations, such - as synchronization and notification logic. - - Platform specific bits (such as actual command for signing the binary) are - to be implemented as a subclass. - - Provides utilities code signing as a whole, including functionality needed - by a signing server and a buildbot worker. - - The signer and builder may run on separate machines, the only requirement is - that they have access to a directory which is shared between them. For the - security concerns this is to be done as a separate machine (or as a Shared - Folder configuration in VirtualBox configuration). This directory might be - mounted under different base paths, but its underlying storage is to be - the same. - - The code signer is short-lived on a buildbot worker side, and is living - forever on a code signing server side. - """ - - # TODO(sergey): Find a neat way to have config annotated. - # config: Config - - # Storage directory where builder puts files which are requested to be - # signed. - # Consider this an input of the code signing server. - unsigned_storage_dir: Path - - # Storage where signed files are stored. - # Consider this an output of the code signer server. - signed_storage_dir: Path - - # Platform the code is currently executing on. - platform: util.Platform - - def __init__(self, config): - self.config = config - - absolute_shared_storage_dir = config.SHARED_STORAGE_DIR.resolve() - - # Unsigned (signing server input) configuration. - self.unsigned_storage_dir = absolute_shared_storage_dir / 'unsigned' - - # Signed (signing server output) configuration. - self.signed_storage_dir = absolute_shared_storage_dir / 'signed' - - self.platform = util.get_current_platform() - - def cleanup_environment_for_builder(self) -> None: - # TODO(sergey): Revisit need of cleaning up the existing files. - # In practice it wasn't so helpful, and with multiple clients - # talking to the same server it becomes even more tricky. - pass - - def cleanup_environment_for_signing_server(self) -> None: - # TODO(sergey): Revisit need of cleaning up the existing files. - # In practice it wasn't so helpful, and with multiple clients - # talking to the same server it becomes even more tricky. - pass - - def generate_request_id(self) -> str: - """ - Generate an unique identifier for code signing request. - """ - return str(uuid.uuid4()) - - def archive_info_for_request_id( - self, path: Path, request_id: str) -> ArchiveWithIndicator: - return ArchiveWithIndicator( - path, f'{request_id}.tar', f'{request_id}.ready') - - def signed_archive_info_for_request_id( - self, request_id: str) -> ArchiveWithIndicator: - return self.archive_info_for_request_id( - self.signed_storage_dir, request_id) - - def unsigned_archive_info_for_request_id( - self, request_id: str) -> ArchiveWithIndicator: - return self.archive_info_for_request_id( - self.unsigned_storage_dir, request_id) - - ############################################################################ - # Buildbot worker side helpers. - - @abc.abstractmethod - def check_file_is_to_be_signed( - self, file: AbsoluteAndRelativeFileName) -> bool: - """ - Check whether file is to be signed. - - Is used by both single file signing pipeline and recursive directory - signing pipeline. - - This is where code signer is to check whether file is to be signed or - not. This check might be based on a simple extension test or on actual - test whether file have a digital signature already or not. - """ - - def collect_files_to_sign(self, path: Path) \ - -> List[AbsoluteAndRelativeFileName]: - """ - Get all files which need to be signed from the given path. - - NOTE: The path might either be a file or directory. - - This function is run from the buildbot worker side. - """ - - # If there is a single file provided trust the buildbot worker that it - # is eligible for signing. - if path.is_file(): - file = AbsoluteAndRelativeFileName.from_path(path) - if not self.check_file_is_to_be_signed(file): - return [] - return [file] - - all_files = AbsoluteAndRelativeFileName.recursively_from_directory( - path) - files_to_be_signed = [file for file in all_files - if self.check_file_is_to_be_signed(file)] - return files_to_be_signed - - def wait_for_signed_archive_or_die(self, request_id) -> None: - """ - Wait until archive with signed files is available. - - Will only return if the archive with signed files is available. If there - was an error during code sign procedure the SystemExit exception is - raised, with the message set to the error reported by the codesign - server. - - Will only wait for the configured time. If that time exceeds and there - is still no responce from the signing server the application will exit - with a non-zero exit code. - - """ - - signed_archive_info = self.signed_archive_info_for_request_id( - request_id) - unsigned_archive_info = self.unsigned_archive_info_for_request_id( - request_id) - - timeout_in_seconds = self.config.TIMEOUT_IN_SECONDS - time_start = time.monotonic() - while not signed_archive_info.is_ready(): - time.sleep(1) - time_slept_in_seconds = time.monotonic() - time_start - if time_slept_in_seconds > timeout_in_seconds: - signed_archive_info.clean() - unsigned_archive_info.clean() - raise SystemExit("Signing server didn't finish signing in " - f'{timeout_in_seconds} seconds, dying :(') - - archive_state = signed_archive_info.get_state() - if archive_state.has_error(): - signed_archive_info.clean() - unsigned_archive_info.clean() - raise SystemExit( - f'Error happenned during codesign procedure: {archive_state.error_message}') - - def copy_signed_files_to_directory( - self, signed_dir: Path, destination_dir: Path) -> None: - """ - Copy all files from signed_dir to destination_dir. - - This function will overwrite any existing file. Permissions are copied - from the source files, but other metadata, such as timestamps, are not. - """ - for signed_filepath in signed_dir.glob('**/*'): - if not signed_filepath.is_file(): - continue - - relative_filepath = signed_filepath.relative_to(signed_dir) - destination_filepath = destination_dir / relative_filepath - destination_filepath.parent.mkdir(parents=True, exist_ok=True) - - shutil.copy(signed_filepath, destination_filepath) - - def run_buildbot_path_sign_pipeline(self, path: Path) -> None: - """ - Run all steps needed to make given path signed. - - Path points to an unsigned file or a directory which contains unsigned - files. - - If the path points to a single file then this file will be signed. - This is used to sign a final bundle such as .msi on Windows or .dmg on - macOS. - - NOTE: The code signed implementation might actually reject signing the - file, in which case the file will be left unsigned. This isn't anything - to be considered a failure situation, just might happen when buildbot - worker can not detect whether signing is really required in a specific - case or not. - - If the path points to a directory then code signer will sign all - signable files from it (finding them recursively). - """ - - self.cleanup_environment_for_builder() - - # Make sure storage directory exists. - self.unsigned_storage_dir.mkdir(parents=True, exist_ok=True) - - # Collect all files which needs to be signed and pack them into a single - # archive which will be sent to the signing server. - logger_builder.info('Collecting files which are to be signed...') - files = self.collect_files_to_sign(path) - if not files: - logger_builder.info('No files to be signed, ignoring.') - return - logger_builder.info('Found %d files to sign.', len(files)) - - request_id = self.generate_request_id() - signed_archive_info = self.signed_archive_info_for_request_id( - request_id) - unsigned_archive_info = self.unsigned_archive_info_for_request_id( - request_id) - - pack_files(files=files, - archive_filepath=unsigned_archive_info.archive_filepath) - unsigned_archive_info.tag_ready() - - # Wait for the signing server to finish signing. - logger_builder.info('Waiting signing server to sign the files...') - self.wait_for_signed_archive_or_die(request_id) - - # Extract signed files from archive and move files to final location. - with TemporaryDirectory(prefix='blender-buildbot-') as temp_dir_str: - unpacked_signed_files_dir = Path(temp_dir_str) - - logger_builder.info('Extracting signed files from archive...') - extract_files( - archive_filepath=signed_archive_info.archive_filepath, - extraction_dir=unpacked_signed_files_dir) - - destination_dir = path - if destination_dir.is_file(): - destination_dir = destination_dir.parent - self.copy_signed_files_to_directory( - unpacked_signed_files_dir, destination_dir) - - logger_builder.info('Removing archive with signed files...') - signed_archive_info.clean() - - ############################################################################ - # Signing server side helpers. - - def wait_for_sign_request(self) -> str: - """ - Wait for the buildbot to request signing of an archive. - - Returns an identifier of signing request. - """ - - # TOOD(sergey): Support graceful shutdown on Ctrl-C. - - logger_server.info( - f'Waiting for a request directory {self.unsigned_storage_dir} to appear.') - while not self.unsigned_storage_dir.exists(): - time.sleep(1) - - logger_server.info( - 'Waiting for a READY indicator of any signing request.') - request_id = None - while request_id is None: - for file in self.unsigned_storage_dir.iterdir(): - if file.suffix != '.ready': - continue - request_id = file.stem - logger_server.info(f'Found READY for request ID {request_id}.') - if request_id is None: - time.sleep(1) - - unsigned_archive_info = self.unsigned_archive_info_for_request_id( - request_id) - while not unsigned_archive_info.is_ready(): - time.sleep(1) - - return request_id - - @abc.abstractmethod - def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - """ - Sign all files in the given directory. - - NOTE: Signing should happen in-place. - """ - - def run_signing_pipeline(self, request_id: str): - """ - Run the full signing pipeline starting from the point when buildbot - worker have requested signing. - """ - - # Make sure storage directory exists. - self.signed_storage_dir.mkdir(parents=True, exist_ok=True) - - with TemporaryDirectory(prefix='blender-codesign-') as temp_dir_str: - temp_dir = Path(temp_dir_str) - - signed_archive_info = self.signed_archive_info_for_request_id( - request_id) - unsigned_archive_info = self.unsigned_archive_info_for_request_id( - request_id) - - logger_server.info('Extracting unsigned files from archive...') - extract_files( - archive_filepath=unsigned_archive_info.archive_filepath, - extraction_dir=temp_dir) - - logger_server.info('Collecting all files which needs signing...') - files = AbsoluteAndRelativeFileName.recursively_from_directory( - temp_dir) - - logger_server.info('Signing all requested files...') - try: - self.sign_all_files(files) - except CodeSignException as error: - signed_archive_info.tag_ready(error_message=error.message) - unsigned_archive_info.clean() - logger_server.info('Signing is complete with errors.') - return - - logger_server.info('Packing signed files...') - pack_files(files=files, - archive_filepath=signed_archive_info.archive_filepath) - signed_archive_info.tag_ready() - - logger_server.info('Removing signing request...') - unsigned_archive_info.clean() - - logger_server.info('Signing is complete.') - - def run_signing_server(self): - logger_server.info('Starting new code signing server...') - self.cleanup_environment_for_signing_server() - logger_server.info('Code signing server is ready') - while True: - logger_server.info('Waiting for the signing request in %s...', - self.unsigned_storage_dir) - request_id = self.wait_for_sign_request() - - logger_server.info( - f'Beging signign procedure for request ID {request_id}.') - self.run_signing_pipeline(request_id) - - ############################################################################ - # Command executing. - # - # Abstracted to a degree that allows to run commands from a foreign - # platform. - # The goal with this is to allow performing dry-run tests of code signer - # server from other platforms (for example, to test that macOS code signer - # does what it is supposed to after doing a refactor on Linux). - - # TODO(sergey): What is the type annotation for the command? - def run_command_or_mock(self, command, platform: util.Platform) -> None: - """ - Run given command if current platform matches given one - - If the platform is different then it will only be printed allowing - to verify logic of the code signing process. - """ - - if platform != self.platform: - logger_server.info( - f'Will run command for {platform}: {command}') - return - - logger_server.info(f'Running command: {command}') - subprocess.run(command) - - # TODO(sergey): What is the type annotation for the command? - def check_output_or_mock(self, command, - platform: util.Platform, - allow_nonzero_exit_code=False) -> str: - """ - Run given command if current platform matches given one - - If the platform is different then it will only be printed allowing - to verify logic of the code signing process. - - If allow_nonzero_exit_code is truth then the output will be returned - even if application quit with non-zero exit code. - Otherwise an subprocess.CalledProcessError exception will be raised - in such case. - """ - - if platform != self.platform: - logger_server.info( - f'Will run command for {platform}: {command}') - return - - if allow_nonzero_exit_code: - process = subprocess.Popen(command, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - output = process.communicate()[0] - return output.decode() - - logger_server.info(f'Running command: {command}') - return subprocess.check_output( - command, stderr=subprocess.STDOUT).decode() diff --git a/build_files/buildbot/codesign/config_builder.py b/build_files/buildbot/codesign/config_builder.py deleted file mode 100644 index 1f41619ba13..00000000000 --- a/build_files/buildbot/codesign/config_builder.py +++ /dev/null @@ -1,62 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# Configuration of a code signer which is specific to the code running from -# buildbot's worker. - -import sys - -from pathlib import Path - -import codesign.util as util - -from codesign.config_common import * - -platform = util.get_current_platform() -if platform == util.Platform.LINUX: - SHARED_STORAGE_DIR = Path('/data/codesign') -elif platform == util.Platform.WINDOWS: - SHARED_STORAGE_DIR = Path('Z:\\codesign') -elif platform == util.Platform.MACOS: - SHARED_STORAGE_DIR = Path('/Volumes/codesign_macos/codesign') - -# https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema -LOGGING = { - 'version': 1, - 'formatters': { - 'default': {'format': '%(asctime)-15s %(levelname)8s %(name)s %(message)s'} - }, - 'handlers': { - 'console': { - 'class': 'logging.StreamHandler', - 'formatter': 'default', - 'stream': 'ext://sys.stderr', - } - }, - 'loggers': { - 'codesign': {'level': 'INFO'}, - }, - 'root': { - 'level': 'WARNING', - 'handlers': [ - 'console', - ], - } -} diff --git a/build_files/buildbot/codesign/config_common.py b/build_files/buildbot/codesign/config_common.py deleted file mode 100644 index a37bc731dc0..00000000000 --- a/build_files/buildbot/codesign/config_common.py +++ /dev/null @@ -1,36 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -from pathlib import Path - -# Timeout in seconds for the signing process. -# -# This is how long buildbot packing step will wait signing server to -# perform signing. -# -# NOTE: Notarization could take a long time, hence the rather high value -# here. Might consider using different timeout for different platforms. -TIMEOUT_IN_SECONDS = 45 * 60 * 60 - -# Directory which is shared across buildbot worker and signing server. -# -# This is where worker puts files requested for signing as well as where -# server puts signed files. -SHARED_STORAGE_DIR: Path diff --git a/build_files/buildbot/codesign/config_server_template.py b/build_files/buildbot/codesign/config_server_template.py deleted file mode 100644 index ff97ed15fa5..00000000000 --- a/build_files/buildbot/codesign/config_server_template.py +++ /dev/null @@ -1,101 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# Configuration of a code signer which is specific to the code signing server. -# -# NOTE: DO NOT put any sensitive information here, put it in an actual -# configuration on the signing machine. - -from pathlib import Path - -from codesign.config_common import * - -CODESIGN_DIRECTORY = Path(__file__).absolute().parent -BLENDER_GIT_ROOT_DIRECTORY = CODESIGN_DIRECTORY.parent.parent.parent - -################################################################################ -# Common configuration. - -# Directory where folders for codesign requests and signed result are stored. -# For example, /data/codesign -SHARED_STORAGE_DIR: Path - -################################################################################ -# macOS-specific configuration. - -MACOS_ENTITLEMENTS_FILE = \ - BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin' / 'entitlements.plist' - -# Identity of the Developer ID Application certificate which is to be used for -# codesign tool. -# Use `security find-identity -v -p codesigning` to find the identity. -# -# NOTE: This identity is just an example from release/darwin/README.txt. -MACOS_CODESIGN_IDENTITY = 'AE825E26F12D08B692F360133210AF46F4CF7B97' - -# User name (Apple ID) which will be used to request notarization. -MACOS_XCRUN_USERNAME = 'me@example.com' - -# One-time application password which will be used to request notarization. -MACOS_XCRUN_PASSWORD = '@keychain:altool-password' - -# Timeout in seconds within which the notarial office is supposed to reply. -MACOS_NOTARIZE_TIMEOUT_IN_SECONDS = 60 * 60 - -################################################################################ -# Windows-specific configuration. - -# URL to the timestamping authority. -WIN_TIMESTAMP_AUTHORITY_URL = 'http://timestamp.digicert.com' - -# Full path to the certificate used for signing. -# -# The path and expected file format might vary depending on a platform. -# -# On Windows it is usually is a PKCS #12 key (.pfx), so the path will look -# like Path('C:\\Secret\\Blender.pfx'). -WIN_CERTIFICATE_FILEPATH: Path - -################################################################################ -# Logging configuration, common for all platforms. - -# https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema -LOGGING = { - 'version': 1, - 'formatters': { - 'default': {'format': '%(asctime)-15s %(levelname)8s %(name)s %(message)s'} - }, - 'handlers': { - 'console': { - 'class': 'logging.StreamHandler', - 'formatter': 'default', - 'stream': 'ext://sys.stderr', - } - }, - 'loggers': { - 'codesign': {'level': 'INFO'}, - }, - 'root': { - 'level': 'WARNING', - 'handlers': [ - 'console', - ], - } -} diff --git a/build_files/buildbot/codesign/exception.py b/build_files/buildbot/codesign/exception.py deleted file mode 100644 index 6c8a9f262a5..00000000000 --- a/build_files/buildbot/codesign/exception.py +++ /dev/null @@ -1,26 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -class CodeSignException(Exception): - message: str - - def __init__(self, message): - self.message = message - super().__init__(self.message) diff --git a/build_files/buildbot/codesign/linux_code_signer.py b/build_files/buildbot/codesign/linux_code_signer.py deleted file mode 100644 index 04935f67832..00000000000 --- a/build_files/buildbot/codesign/linux_code_signer.py +++ /dev/null @@ -1,72 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# NOTE: This is a no-op signer (since there isn't really a procedure to sign -# Linux binaries yet). Used to debug and verify the code signing routines on -# a Linux environment. - -import logging - -from pathlib import Path -from typing import List - -from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName -from codesign.base_code_signer import BaseCodeSigner - -logger = logging.getLogger(__name__) -logger_server = logger.getChild('server') - - -class LinuxCodeSigner(BaseCodeSigner): - def is_active(self) -> bool: - """ - Check whether this signer is active. - - if it is inactive, no files will be signed. - - Is used to be able to debug code signing pipeline on Linux, where there - is no code signing happening in the actual buildbot and release - environment. - """ - return False - - def check_file_is_to_be_signed( - self, file: AbsoluteAndRelativeFileName) -> bool: - if file.relative_filepath == Path('blender'): - return True - if (file.relative_filepath.parts[-3:-1] == ('python', 'bin') and - file.relative_filepath.name.startwith('python')): - return True - if file.relative_filepath.suffix == '.so': - return True - return False - - def collect_files_to_sign(self, path: Path) \ - -> List[AbsoluteAndRelativeFileName]: - if not self.is_active(): - return [] - - return super().collect_files_to_sign(path) - - def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - num_files = len(files) - for file_index, file in enumerate(files): - logger.info('Server: Signed file [%d/%d] %s', - file_index + 1, num_files, file.relative_filepath) diff --git a/build_files/buildbot/codesign/macos_code_signer.py b/build_files/buildbot/codesign/macos_code_signer.py deleted file mode 100644 index f03dad8e1d6..00000000000 --- a/build_files/buildbot/codesign/macos_code_signer.py +++ /dev/null @@ -1,456 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -import logging -import re -import stat -import subprocess -import time - -from pathlib import Path -from typing import List - -import codesign.util as util - -from buildbot_utils import Builder - -from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName -from codesign.base_code_signer import BaseCodeSigner -from codesign.exception import CodeSignException - -logger = logging.getLogger(__name__) -logger_server = logger.getChild('server') - -# NOTE: Check is done as filename.endswith(), so keep the dot -EXTENSIONS_TO_BE_SIGNED = {'.dylib', '.so', '.dmg'} - -# Prefixes of a file (not directory) name which are to be signed. -# Used to sign extra executable files in Contents/Resources. -NAME_PREFIXES_TO_BE_SIGNED = {'python'} - - -class NotarizationException(CodeSignException): - pass - - -def is_file_from_bundle(file: AbsoluteAndRelativeFileName) -> bool: - """ - Check whether file is coming from an .app bundle - """ - parts = file.relative_filepath.parts - if not parts: - return False - if not parts[0].endswith('.app'): - return False - return True - - -def get_bundle_from_file( - file: AbsoluteAndRelativeFileName) -> AbsoluteAndRelativeFileName: - """ - Get AbsoluteAndRelativeFileName descriptor of bundle - """ - assert(is_file_from_bundle(file)) - - parts = file.relative_filepath.parts - bundle_name = parts[0] - - base_dir = file.base_dir - bundle_filepath = file.base_dir / bundle_name - return AbsoluteAndRelativeFileName(base_dir, bundle_filepath) - - -def is_bundle_executable_file(file: AbsoluteAndRelativeFileName) -> bool: - """ - Check whether given file is an executable within an app bundle - """ - if not is_file_from_bundle(file): - return False - - parts = file.relative_filepath.parts - num_parts = len(parts) - if num_parts < 3: - return False - - if parts[1:3] != ('Contents', 'MacOS'): - return False - - return True - - -def xcrun_field_value_from_output(field: str, output: str) -> str: - """ - Get value of a given field from xcrun output. - - If field is not found empty string is returned. - """ - - field_prefix = field + ': ' - for line in output.splitlines(): - line = line.strip() - if line.startswith(field_prefix): - return line[len(field_prefix):] - return '' - - -class MacOSCodeSigner(BaseCodeSigner): - def check_file_is_to_be_signed( - self, file: AbsoluteAndRelativeFileName) -> bool: - if file.relative_filepath.name.startswith('.'): - return False - - if is_bundle_executable_file(file): - return True - - base_name = file.relative_filepath.name - if any(base_name.startswith(prefix) - for prefix in NAME_PREFIXES_TO_BE_SIGNED): - return True - - mode = file.absolute_filepath.lstat().st_mode - if mode & stat.S_IXUSR != 0: - file_output = subprocess.check_output( - ("file", file.absolute_filepath)).decode() - if "64-bit executable" in file_output: - return True - - return file.relative_filepath.suffix in EXTENSIONS_TO_BE_SIGNED - - def collect_files_to_sign(self, path: Path) \ - -> List[AbsoluteAndRelativeFileName]: - # Include all files when signing app or dmg bundle: all the files are - # needed to do valid signature of bundle. - if path.name.endswith('.app'): - return AbsoluteAndRelativeFileName.recursively_from_directory(path) - if path.is_dir(): - files = [] - for child in path.iterdir(): - if child.name.endswith('.app'): - current_files = AbsoluteAndRelativeFileName.recursively_from_directory( - child) - else: - current_files = super().collect_files_to_sign(child) - for current_file in current_files: - files.append(AbsoluteAndRelativeFileName( - path, current_file.absolute_filepath)) - return files - return super().collect_files_to_sign(path) - - ############################################################################ - # Codesign. - - def codesign_remove_signature( - self, file: AbsoluteAndRelativeFileName) -> None: - """ - Make sure given file does not have codesign signature - - This is needed because codesigning is not possible for file which has - signature already. - """ - - logger_server.info( - 'Removing codesign signature from %s...', file.relative_filepath) - - command = ['codesign', '--remove-signature', file.absolute_filepath] - self.run_command_or_mock(command, util.Platform.MACOS) - - def codesign_file( - self, file: AbsoluteAndRelativeFileName) -> None: - """ - Sign given file - - NOTE: File must not have any signatures. - """ - - logger_server.info( - 'Codesigning %s...', file.relative_filepath) - - entitlements_file = self.config.MACOS_ENTITLEMENTS_FILE - command = ['codesign', - '--timestamp', - '--options', 'runtime', - f'--entitlements={entitlements_file}', - '--sign', self.config.MACOS_CODESIGN_IDENTITY, - file.absolute_filepath] - self.run_command_or_mock(command, util.Platform.MACOS) - - def codesign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - """ - Run codesign tool on all eligible files in the given list. - - Will ignore all files which are not to be signed. For the rest will - remove possible existing signature and add a new signature. - """ - - num_files = len(files) - have_ignored_files = False - signed_files = [] - for file_index, file in enumerate(files): - # Ignore file if it is not to be signed. - # Allows to manually construct ZIP of a bundle and get it signed. - if not self.check_file_is_to_be_signed(file): - logger_server.info( - 'Ignoring file [%d/%d] %s', - file_index + 1, num_files, file.relative_filepath) - have_ignored_files = True - continue - - logger_server.info( - 'Running codesigning routines for file [%d/%d] %s...', - file_index + 1, num_files, file.relative_filepath) - - self.codesign_remove_signature(file) - self.codesign_file(file) - - signed_files.append(file) - - if have_ignored_files: - logger_server.info('Signed %d files:', len(signed_files)) - num_signed_files = len(signed_files) - for file_index, signed_file in enumerate(signed_files): - logger_server.info( - '- [%d/%d] %s', - file_index + 1, num_signed_files, - signed_file.relative_filepath) - - def codesign_bundles( - self, files: List[AbsoluteAndRelativeFileName]) -> None: - """ - Codesign all .app bundles in the given list of files. - - Bundle is deducted from paths of the files, and every bundle is only - signed once. - """ - - signed_bundles = set() - extra_files = [] - - for file in files: - if not is_file_from_bundle(file): - continue - bundle = get_bundle_from_file(file) - bundle_name = bundle.relative_filepath - if bundle_name in signed_bundles: - continue - - logger_server.info('Running codesign routines on bundle %s', - bundle_name) - - # It is not possible to remove signature from DMG. - if bundle.relative_filepath.name.endswith('.app'): - self.codesign_remove_signature(bundle) - self.codesign_file(bundle) - - signed_bundles.add(bundle_name) - - # Codesign on a bundle adds an extra folder with information. - # It needs to be compied to the source. - code_signature_directory = \ - bundle.absolute_filepath / 'Contents' / '_CodeSignature' - code_signature_files = \ - AbsoluteAndRelativeFileName.recursively_from_directory( - code_signature_directory) - for code_signature_file in code_signature_files: - bundle_relative_file = AbsoluteAndRelativeFileName( - bundle.base_dir, - code_signature_directory / - code_signature_file.relative_filepath) - extra_files.append(bundle_relative_file) - - files.extend(extra_files) - - ############################################################################ - # Notarization. - - def notarize_get_bundle_id(self, file: AbsoluteAndRelativeFileName) -> str: - """ - Get bundle ID which will be used to notarize DMG - """ - name = file.relative_filepath.name - app_name = name.split('-', 2)[0].lower() - - app_name_words = app_name.split() - if len(app_name_words) > 1: - app_name_id = ''.join(word.capitalize() for word in app_name_words) - else: - app_name_id = app_name_words[0] - - # TODO(sergey): Consider using "alpha" for buildbot builds. - return f'org.blenderfoundation.{app_name_id}.release' - - def notarize_request(self, file) -> str: - """ - Request notarization of the given file. - - Returns UUID of the notarization request. If error occurred None is - returned instead of UUID. - """ - - bundle_id = self.notarize_get_bundle_id(file) - logger_server.info('Bundle ID: %s', bundle_id) - - logger_server.info('Submitting file to the notarial office.') - command = [ - 'xcrun', 'altool', '--notarize-app', '--verbose', - '-f', file.absolute_filepath, - '--primary-bundle-id', bundle_id, - '--username', self.config.MACOS_XCRUN_USERNAME, - '--password', self.config.MACOS_XCRUN_PASSWORD] - - output = self.check_output_or_mock( - command, util.Platform.MACOS, allow_nonzero_exit_code=True) - - for line in output.splitlines(): - line = line.strip() - if line.startswith('RequestUUID = '): - request_uuid = line[14:] - return request_uuid - - # Check whether the package has been already submitted. - if 'The software asset has already been uploaded.' in line: - request_uuid = re.sub( - '.*The upload ID is ([A-Fa-f0-9\-]+).*', '\\1', line) - logger_server.warning( - f'The package has been already submitted under UUID {request_uuid}') - return request_uuid - - logger_server.error(output) - logger_server.error('xcrun command did not report RequestUUID') - return None - - def notarize_review_status(self, xcrun_output: str) -> bool: - """ - Review status returned by xcrun's notarization info - - Returns truth if the notarization process has finished. - If there are errors during notarization, a NotarizationException() - exception is thrown with status message from the notarial office. - """ - - # Parse status and message - status = xcrun_field_value_from_output('Status', xcrun_output) - status_message = xcrun_field_value_from_output( - 'Status Message', xcrun_output) - - if status == 'success': - logger_server.info( - 'Package successfully notarized: %s', status_message) - return True - - if status == 'invalid': - logger_server.error(xcrun_output) - logger_server.error( - 'Package notarization has failed: %s', status_message) - raise NotarizationException(status_message) - - if status == 'in progress': - return False - - logger_server.info( - 'Unknown notarization status %s (%s)', status, status_message) - - return False - - def notarize_wait_result(self, request_uuid: str) -> None: - """ - Wait for until notarial office have a reply - """ - - logger_server.info( - 'Waiting for a result from the notarization office.') - - command = ['xcrun', 'altool', - '--notarization-info', request_uuid, - '--username', self.config.MACOS_XCRUN_USERNAME, - '--password', self.config.MACOS_XCRUN_PASSWORD] - - time_start = time.monotonic() - timeout_in_seconds = self.config.MACOS_NOTARIZE_TIMEOUT_IN_SECONDS - - while True: - xcrun_output = self.check_output_or_mock( - command, util.Platform.MACOS, allow_nonzero_exit_code=True) - - if self.notarize_review_status(xcrun_output): - break - - logger_server.info('Keep waiting for notarization office.') - time.sleep(30) - - time_slept_in_seconds = time.monotonic() - time_start - if time_slept_in_seconds > timeout_in_seconds: - logger_server.error( - "Notarial office didn't reply in %f seconds.", - timeout_in_seconds) - - def notarize_staple(self, file: AbsoluteAndRelativeFileName) -> bool: - """ - Staple notarial label on the file - """ - - logger_server.info('Stapling notarial stamp.') - - command = ['xcrun', 'stapler', 'staple', '-v', file.absolute_filepath] - self.check_output_or_mock(command, util.Platform.MACOS) - - def notarize_dmg(self, file: AbsoluteAndRelativeFileName) -> bool: - """ - Run entire pipeline to get DMG notarized. - """ - logger_server.info('Begin notarization routines on %s', - file.relative_filepath) - - # Submit file for notarization. - request_uuid = self.notarize_request(file) - if not request_uuid: - return False - logger_server.info('Received Request UUID: %s', request_uuid) - - # Wait for the status from the notarization office. - if not self.notarize_wait_result(request_uuid): - return False - - # Staple. - self.notarize_staple(file) - - def notarize_all_dmg( - self, files: List[AbsoluteAndRelativeFileName]) -> bool: - """ - Notarize all DMG images from the input. - - Images are supposed to be codesigned already. - """ - for file in files: - if not file.relative_filepath.name.endswith('.dmg'): - continue - if not self.check_file_is_to_be_signed(file): - continue - - self.notarize_dmg(file) - - ############################################################################ - # Entry point. - - def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - # TODO(sergey): Handle errors somehow. - - self.codesign_all_files(files) - self.codesign_bundles(files) - self.notarize_all_dmg(files) diff --git a/build_files/buildbot/codesign/simple_code_signer.py b/build_files/buildbot/codesign/simple_code_signer.py deleted file mode 100644 index 674d9e9ce9e..00000000000 --- a/build_files/buildbot/codesign/simple_code_signer.py +++ /dev/null @@ -1,52 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - - -import logging.config -import sys - -from pathlib import Path -from typing import Optional - -import codesign.config_builder -import codesign.util as util -from codesign.base_code_signer import BaseCodeSigner - - -class SimpleCodeSigner: - code_signer: Optional[BaseCodeSigner] - - def __init__(self): - platform = util.get_current_platform() - if platform == util.Platform.LINUX: - from codesign.linux_code_signer import LinuxCodeSigner - self.code_signer = LinuxCodeSigner(codesign.config_builder) - elif platform == util.Platform.MACOS: - from codesign.macos_code_signer import MacOSCodeSigner - self.code_signer = MacOSCodeSigner(codesign.config_builder) - elif platform == util.Platform.WINDOWS: - from codesign.windows_code_signer import WindowsCodeSigner - self.code_signer = WindowsCodeSigner(codesign.config_builder) - else: - self.code_signer = None - - def sign_file_or_directory(self, path: Path) -> None: - logging.config.dictConfig(codesign.config_builder.LOGGING) - self.code_signer.run_buildbot_path_sign_pipeline(path) diff --git a/build_files/buildbot/codesign/util.py b/build_files/buildbot/codesign/util.py deleted file mode 100644 index e67292dd049..00000000000 --- a/build_files/buildbot/codesign/util.py +++ /dev/null @@ -1,54 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -import sys - -from enum import Enum -from pathlib import Path - - -class Platform(Enum): - LINUX = 1 - MACOS = 2 - WINDOWS = 3 - - -def get_current_platform() -> Platform: - if sys.platform == 'linux': - return Platform.LINUX - elif sys.platform == 'darwin': - return Platform.MACOS - elif sys.platform == 'win32': - return Platform.WINDOWS - raise Exception(f'Unknown platform {sys.platform}') - - -def ensure_file_does_not_exist_or_die(filepath: Path) -> None: - """ - If the file exists, unlink it. - If the file path exists and is not a file an assert will trigger. - If the file path does not exists nothing happens. - """ - if not filepath.exists(): - return - if not filepath.is_file(): - # TODO(sergey): Provide information about what the filepath actually is. - raise SystemExit(f'{filepath} is expected to be a file, but is not') - filepath.unlink() diff --git a/build_files/buildbot/codesign/windows_code_signer.py b/build_files/buildbot/codesign/windows_code_signer.py deleted file mode 100644 index db185788a56..00000000000 --- a/build_files/buildbot/codesign/windows_code_signer.py +++ /dev/null @@ -1,117 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -import logging - -from pathlib import Path -from typing import List - -import codesign.util as util - -from buildbot_utils import Builder - -from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName -from codesign.base_code_signer import BaseCodeSigner -from codesign.exception import CodeSignException - -logger = logging.getLogger(__name__) -logger_server = logger.getChild('server') - -# NOTE: Check is done as filename.endswith(), so keep the dot -EXTENSIONS_TO_BE_SIGNED = {'.exe', '.dll', '.pyd', '.msi'} - -BLACKLIST_FILE_PREFIXES = ( - 'api-ms-', 'concrt', 'msvcp', 'ucrtbase', 'vcomp', 'vcruntime') - - -class SigntoolException(CodeSignException): - pass - -class WindowsCodeSigner(BaseCodeSigner): - def check_file_is_to_be_signed( - self, file: AbsoluteAndRelativeFileName) -> bool: - base_name = file.relative_filepath.name - if any(base_name.startswith(prefix) - for prefix in BLACKLIST_FILE_PREFIXES): - return False - - return file.relative_filepath.suffix in EXTENSIONS_TO_BE_SIGNED - - - def get_sign_command_prefix(self) -> List[str]: - return [ - 'signtool', 'sign', '/v', - '/f', self.config.WIN_CERTIFICATE_FILEPATH, - '/tr', self.config.WIN_TIMESTAMP_AUTHORITY_URL] - - - def run_codesign_tool(self, filepath: Path) -> None: - command = self.get_sign_command_prefix() + [filepath] - - try: - codesign_output = self.check_output_or_mock(command, util.Platform.WINDOWS) - except subprocess.CalledProcessError as e: - raise SigntoolException(f'Error running signtool {e}') - - logger_server.info(f'signtool output:\n{codesign_output}') - - got_number_of_success = False - - for line in codesign_output.split('\n'): - line_clean = line.strip() - line_clean_lower = line_clean.lower() - - if line_clean_lower.startswith('number of warnings') or \ - line_clean_lower.startswith('number of errors'): - number = int(line_clean_lower.split(':')[1]) - if number != 0: - raise SigntoolException('Non-clean success of signtool') - - if line_clean_lower.startswith('number of files successfully signed'): - got_number_of_success = True - number = int(line_clean_lower.split(':')[1]) - if number != 1: - raise SigntoolException('Signtool did not consider codesign a success') - - if not got_number_of_success: - raise SigntoolException('Signtool did not report number of files signed') - - - def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None: - # NOTE: Sign files one by one to avoid possible command line length - # overflow (which could happen if we ever decide to sign every binary - # in the install folder, for example). - # - # TODO(sergey): Consider doing batched signing of handful of files in - # one go (but only if this actually known to be much faster). - num_files = len(files) - for file_index, file in enumerate(files): - # Ignore file if it is not to be signed. - # Allows to manually construct ZIP of package and get it signed. - if not self.check_file_is_to_be_signed(file): - logger_server.info( - 'Ignoring file [%d/%d] %s', - file_index + 1, num_files, file.relative_filepath) - continue - - logger_server.info( - 'Running signtool command for file [%d/%d] %s...', - file_index + 1, num_files, file.relative_filepath) - self.run_codesign_tool(file.absolute_filepath) diff --git a/build_files/buildbot/codesign_server_linux.py b/build_files/buildbot/codesign_server_linux.py deleted file mode 100755 index be3065e640d..00000000000 --- a/build_files/buildbot/codesign_server_linux.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# NOTE: This is a no-op signer (since there isn't really a procedure to sign -# Linux binaries yet). Used to debug and verify the code signing routines on -# a Linux environment. - -import logging.config -from pathlib import Path -from typing import List - -from codesign.linux_code_signer import LinuxCodeSigner -import codesign.config_server - -if __name__ == "__main__": - logging.config.dictConfig(codesign.config_server.LOGGING) - code_signer = LinuxCodeSigner(codesign.config_server) - code_signer.run_signing_server() diff --git a/build_files/buildbot/codesign_server_macos.py b/build_files/buildbot/codesign_server_macos.py deleted file mode 100755 index 1bdb012fe67..00000000000 --- a/build_files/buildbot/codesign_server_macos.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -import logging.config -from pathlib import Path -from typing import List - -from codesign.macos_code_signer import MacOSCodeSigner -import codesign.config_server - -if __name__ == "__main__": - entitlements_file = codesign.config_server.MACOS_ENTITLEMENTS_FILE - if not entitlements_file.exists(): - raise SystemExit( - 'Entitlements file {entitlements_file} does not exist.') - if not entitlements_file.is_file(): - raise SystemExit( - 'Entitlements file {entitlements_file} is not a file.') - - logging.config.dictConfig(codesign.config_server.LOGGING) - code_signer = MacOSCodeSigner(codesign.config_server) - code_signer.run_signing_server() diff --git a/build_files/buildbot/codesign_server_windows.bat b/build_files/buildbot/codesign_server_windows.bat deleted file mode 100644 index 82680f30eb4..00000000000 --- a/build_files/buildbot/codesign_server_windows.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -rem This is an entry point of the codesign server for Windows. -rem It makes sure that signtool.exe is within the current PATH and can be -rem used by the Python script. - -SETLOCAL - -set PATH=C:\Program Files (x86)\Windows Kits\10\App Certification Kit;%PATH% - -codesign_server_windows.py diff --git a/build_files/buildbot/codesign_server_windows.py b/build_files/buildbot/codesign_server_windows.py deleted file mode 100755 index 97ea4fd6756..00000000000 --- a/build_files/buildbot/codesign_server_windows.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# Implementation of codesign server for Windows. -# -# NOTE: If signtool.exe is not in the PATH use codesign_server_windows.bat - -import logging.config -import shutil - -from pathlib import Path -from typing import List - -import codesign.util as util - -from codesign.windows_code_signer import WindowsCodeSigner -import codesign.config_server - -if __name__ == "__main__": - logging.config.dictConfig(codesign.config_server.LOGGING) - - logger = logging.getLogger(__name__) - logger_server = logger.getChild('server') - - # TODO(sergey): Consider moving such sanity checks into - # CodeSigner.check_environment_or_die(). - if not shutil.which('signtool.exe'): - if util.get_current_platform() == util.Platform.WINDOWS: - raise SystemExit("signtool.exe is not found in %PATH%") - logger_server.info( - 'signtool.exe not found, ' - 'but will not be used on this foreign platform') - - code_signer = WindowsCodeSigner(codesign.config_server) - code_signer.run_signing_server() diff --git a/build_files/buildbot/worker_bundle_dmg.py b/build_files/buildbot/worker_bundle_dmg.py deleted file mode 100755 index 56e0d7da88e..00000000000 --- a/build_files/buildbot/worker_bundle_dmg.py +++ /dev/null @@ -1,551 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -import argparse -import re -import shutil -import subprocess -import sys -import time - -from pathlib import Path -from tempfile import TemporaryDirectory, NamedTemporaryFile -from typing import List - -BUILDBOT_DIRECTORY = Path(__file__).absolute().parent -CODESIGN_SCRIPT = BUILDBOT_DIRECTORY / 'worker_codesign.py' -BLENDER_GIT_ROOT_DIRECTORY = BUILDBOT_DIRECTORY.parent.parent -DARWIN_DIRECTORY = BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin' - - -# Extra size which is added on top of actual files size when estimating size -# of destination DNG. -EXTRA_DMG_SIZE_IN_BYTES = 800 * 1024 * 1024 - -################################################################################ -# Common utilities - - -def get_directory_size(root_directory: Path) -> int: - """ - Get size of directory on disk - """ - - total_size = 0 - for file in root_directory.glob('**/*'): - total_size += file.lstat().st_size - return total_size - - -################################################################################ -# DMG bundling specific logic - -def create_argument_parser(): - parser = argparse.ArgumentParser() - parser.add_argument( - 'source_dir', - type=Path, - help='Source directory which points to either existing .app bundle' - 'or to a directory with .app bundles.') - parser.add_argument( - '--background-image', - type=Path, - help="Optional background picture which will be set on the DMG." - "If not provided default Blender's one is used.") - parser.add_argument( - '--volume-name', - type=str, - help='Optional name of a volume which will be used for DMG.') - parser.add_argument( - '--dmg', - type=Path, - help='Optional argument which points to a final DMG file name.') - parser.add_argument( - '--applescript', - type=Path, - help="Optional path to applescript to set up folder looks of DMG." - "If not provided default Blender's one is used.") - parser.add_argument( - '--codesign', - action="store_true", - help="Code sign and notarize DMG contents.") - return parser - - -def collect_app_bundles(source_dir: Path) -> List[Path]: - """ - Collect all app bundles which are to be put into DMG - - If the source directory points to FOO.app it will be the only app bundle - packed. - - Otherwise all .app bundles from given directory are placed to a single - DMG. - """ - - if source_dir.name.endswith('.app'): - return [source_dir] - - app_bundles = [] - for filename in source_dir.glob('*'): - if not filename.is_dir(): - continue - if not filename.name.endswith('.app'): - continue - - app_bundles.append(filename) - - return app_bundles - - -def collect_and_log_app_bundles(source_dir: Path) -> List[Path]: - app_bundles = collect_app_bundles(source_dir) - - if not app_bundles: - print('No app bundles found for packing') - return - - print(f'Found {len(app_bundles)} to pack:') - for app_bundle in app_bundles: - print(f'- {app_bundle}') - - return app_bundles - - -def estimate_dmg_size(app_bundles: List[Path]) -> int: - """ - Estimate size of DMG to hold requested app bundles - - The size is based on actual size of all files in all bundles plus some - space to compensate for different size-on-disk plus some space to hold - codesign signatures. - - Is better to be on a high side since the empty space is compressed, but - lack of space might cause silent failures later on. - """ - - app_bundles_size = 0 - for app_bundle in app_bundles: - app_bundles_size += get_directory_size(app_bundle) - - return app_bundles_size + EXTRA_DMG_SIZE_IN_BYTES - - -def copy_app_bundles_to_directory(app_bundles: List[Path], - directory: Path) -> None: - """ - Copy all bundles to a given directory - - This directory is what the DMG will be created from. - """ - for app_bundle in app_bundles: - print(f'Copying {app_bundle.name}...') - shutil.copytree(app_bundle, directory / app_bundle.name) - - -def get_main_app_bundle(app_bundles: List[Path]) -> Path: - """ - Get application bundle main for the installation - """ - return app_bundles[0] - - -def create_dmg_image(app_bundles: List[Path], - dmg_filepath: Path, - volume_name: str) -> None: - """ - Create DMG disk image and put app bundles in it - - No DMG configuration or codesigning is happening here. - """ - - if dmg_filepath.exists(): - print(f'Removing existing writable DMG {dmg_filepath}...') - dmg_filepath.unlink() - - print('Preparing directory with app bundles for the DMG...') - with TemporaryDirectory(prefix='blender-dmg-content-') as content_dir_str: - # Copy all bundles to a clean directory. - content_dir = Path(content_dir_str) - copy_app_bundles_to_directory(app_bundles, content_dir) - - # Estimate size of the DMG. - dmg_size = estimate_dmg_size(app_bundles) - print(f'Estimated DMG size: {dmg_size:,} bytes.') - - # Create the DMG. - print(f'Creating writable DMG {dmg_filepath}') - command = ('hdiutil', - 'create', - '-size', str(dmg_size), - '-fs', 'HFS+', - '-srcfolder', content_dir, - '-volname', volume_name, - '-format', 'UDRW', - dmg_filepath) - subprocess.run(command) - - -def get_writable_dmg_filepath(dmg_filepath: Path): - """ - Get file path for writable DMG image - """ - parent = dmg_filepath.parent - return parent / (dmg_filepath.stem + '-temp.dmg') - - -def mount_readwrite_dmg(dmg_filepath: Path) -> None: - """ - Mount writable DMG - - Mounting point would be /Volumes/ - """ - - print(f'Mounting read-write DMG ${dmg_filepath}') - command = ('hdiutil', - 'attach', '-readwrite', - '-noverify', - '-noautoopen', - dmg_filepath) - subprocess.run(command) - - -def get_mount_directory_for_volume_name(volume_name: str) -> Path: - """ - Get directory under which the volume will be mounted - """ - - return Path('/Volumes') / volume_name - - -def eject_volume(volume_name: str) -> None: - """ - Eject given volume, if mounted - """ - mount_directory = get_mount_directory_for_volume_name(volume_name) - if not mount_directory.exists(): - return - mount_directory_str = str(mount_directory) - - print(f'Ejecting volume {volume_name}') - - # Figure out which device to eject. - mount_output = subprocess.check_output(['mount']).decode() - device = '' - for line in mount_output.splitlines(): - if f'on {mount_directory_str} (' not in line: - continue - tokens = line.split(' ', 3) - if len(tokens) < 3: - continue - if tokens[1] != 'on': - continue - if device: - raise Exception( - f'Multiple devices found for mounting point {mount_directory}') - device = tokens[0] - - if not device: - raise Exception( - f'No device found for mounting point {mount_directory}') - - print(f'{mount_directory} is mounted as device {device}, ejecting...') - subprocess.run(['diskutil', 'eject', device]) - - -def copy_background_if_needed(background_image_filepath: Path, - mount_directory: Path) -> None: - """ - Copy background to the DMG - - If the background image is not specified it will not be copied. - """ - - if not background_image_filepath: - print('No background image provided.') - return - - print(f'Copying background image {background_image_filepath}') - - destination_dir = mount_directory / '.background' - destination_dir.mkdir(exist_ok=True) - - destination_filepath = destination_dir / background_image_filepath.name - shutil.copy(background_image_filepath, destination_filepath) - - -def create_applications_link(mount_directory: Path) -> None: - """ - Create link to /Applications in the given location - """ - - print('Creating link to /Applications') - - command = ('ln', '-s', '/Applications', mount_directory / ' ') - subprocess.run(command) - - -def run_applescript(applescript: Path, - volume_name: str, - app_bundles: List[Path], - background_image_filepath: Path) -> None: - """ - Run given applescript to adjust look and feel of the DMG - """ - - main_app_bundle = get_main_app_bundle(app_bundles) - - with NamedTemporaryFile( - mode='w', suffix='.applescript') as temp_applescript: - print('Adjusting applescript for volume name...') - # Adjust script to the specific volume name. - with open(applescript, mode='r') as input: - for line in input.readlines(): - stripped_line = line.strip() - if stripped_line.startswith('tell disk'): - line = re.sub('tell disk ".*"', - f'tell disk "{volume_name}"', - line) - elif stripped_line.startswith('set background picture'): - if not background_image_filepath: - continue - else: - background_image_short = \ - '.background:' + background_image_filepath.name - line = re.sub('to file ".*"', - f'to file "{background_image_short}"', - line) - line = line.replace('blender.app', main_app_bundle.name) - temp_applescript.write(line) - - temp_applescript.flush() - - print('Running applescript...') - command = ('osascript', temp_applescript.name) - subprocess.run(command) - - print('Waiting for applescript...') - - # NOTE: This is copied from bundle.sh. The exact reason for sleep is - # still remained a mystery. - time.sleep(5) - - -def codesign(subject: Path): - """ - Codesign file or directory - - NOTE: For DMG it will also notarize. - """ - - command = (CODESIGN_SCRIPT, subject) - subprocess.run(command) - - -def codesign_app_bundles_in_dmg(mount_directory: str) -> None: - """ - Code sign all binaries and bundles in the mounted directory - """ - - print(f'Codesigning all app bundles in {mount_directory}') - codesign(mount_directory) - - -def codesign_and_notarize_dmg(dmg_filepath: Path) -> None: - """ - Run codesign and notarization pipeline on the DMG - """ - - print(f'Codesigning and notarizing DMG {dmg_filepath}') - codesign(dmg_filepath) - - -def compress_dmg(writable_dmg_filepath: Path, - final_dmg_filepath: Path) -> None: - """ - Compress temporary read-write DMG - """ - command = ('hdiutil', 'convert', - writable_dmg_filepath, - '-format', 'UDZO', - '-o', final_dmg_filepath) - - if final_dmg_filepath.exists(): - print(f'Removing old compressed DMG {final_dmg_filepath}') - final_dmg_filepath.unlink() - - print('Compressing disk image...') - subprocess.run(command) - - -def create_final_dmg(app_bundles: List[Path], - dmg_filepath: Path, - background_image_filepath: Path, - volume_name: str, - applescript: Path, - codesign: bool) -> None: - """ - Create DMG with all app bundles - - Will take care configuring background, signing all binaries and app bundles - and notarizing the DMG. - """ - - print('Running all routines to create final DMG') - - writable_dmg_filepath = get_writable_dmg_filepath(dmg_filepath) - mount_directory = get_mount_directory_for_volume_name(volume_name) - - # Make sure volume is not mounted. - # If it is mounted it will prevent removing old DMG files and could make - # it so app bundles are copied to the wrong place. - eject_volume(volume_name) - - create_dmg_image(app_bundles, writable_dmg_filepath, volume_name) - - mount_readwrite_dmg(writable_dmg_filepath) - - # Run codesign first, prior to copying amything else. - # - # This allows to recurs into the content of bundles without worrying about - # possible interfereice of Application symlink. - if codesign: - codesign_app_bundles_in_dmg(mount_directory) - - copy_background_if_needed(background_image_filepath, mount_directory) - create_applications_link(mount_directory) - run_applescript(applescript, volume_name, app_bundles, - background_image_filepath) - - print('Ejecting read-write DMG image...') - eject_volume(volume_name) - - compress_dmg(writable_dmg_filepath, dmg_filepath) - writable_dmg_filepath.unlink() - - if codesign: - codesign_and_notarize_dmg(dmg_filepath) - - -def ensure_dmg_extension(filepath: Path) -> Path: - """ - Make sure given file have .dmg extension - """ - - if filepath.suffix != '.dmg': - return filepath.with_suffix(f'{filepath.suffix}.dmg') - return filepath - - -def get_dmg_filepath(requested_name: Path, app_bundles: List[Path]) -> Path: - """ - Get full file path for the final DMG image - - Will use the provided one when possible, otherwise will deduct it from - app bundles. - - If the name is deducted, the DMG is stored in the current directory. - """ - - if requested_name: - return ensure_dmg_extension(requested_name.absolute()) - - # TODO(sergey): This is not necessarily the main one. - main_bundle = app_bundles[0] - # Strip .app from the name - return Path(main_bundle.name[:-4] + '.dmg').absolute() - - -def get_background_image(requested_background_image: Path) -> Path: - """ - Get effective filepath for the background image - """ - - if requested_background_image: - return requested_background_image.absolute() - - return DARWIN_DIRECTORY / 'background.tif' - - -def get_applescript(requested_applescript: Path) -> Path: - """ - Get effective filepath for the applescript - """ - - if requested_applescript: - return requested_applescript.absolute() - - return DARWIN_DIRECTORY / 'blender.applescript' - - -def get_volume_name_from_dmg_filepath(dmg_filepath: Path) -> str: - """ - Deduct volume name from the DMG path - - Will use first part of the DMG file name prior to dash. - """ - - tokens = dmg_filepath.stem.split('-') - words = tokens[0].split() - - return ' '.join(word.capitalize() for word in words) - - -def get_volume_name(requested_volume_name: str, - dmg_filepath: Path) -> str: - """ - Get effective name for DMG volume - """ - - if requested_volume_name: - return requested_volume_name - - return get_volume_name_from_dmg_filepath(dmg_filepath) - - -def main(): - parser = create_argument_parser() - args = parser.parse_args() - - # Get normalized input parameters. - source_dir = args.source_dir.absolute() - background_image_filepath = get_background_image(args.background_image) - applescript = get_applescript(args.applescript) - codesign = args.codesign - - app_bundles = collect_and_log_app_bundles(source_dir) - if not app_bundles: - return - - dmg_filepath = get_dmg_filepath(args.dmg, app_bundles) - volume_name = get_volume_name(args.volume_name, dmg_filepath) - - print(f'Will produce DMG "{dmg_filepath.name}" (without quotes)') - - create_final_dmg(app_bundles, - dmg_filepath, - background_image_filepath, - volume_name, - applescript, - codesign) - - -if __name__ == "__main__": - main() diff --git a/build_files/buildbot/worker_codesign.cmake b/build_files/buildbot/worker_codesign.cmake deleted file mode 100644 index f37feaef407..00000000000 --- a/build_files/buildbot/worker_codesign.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# This is a script which is used as POST-INSTALL one for regular CMake's -# INSTALL target. -# It is used by buildbot workers to sign every binary which is going into -# the final buundle. - -# On Windows Python 3 there only is python.exe, no python3.exe. -# -# On other platforms it is possible to have python2 and python3, and a -# symbolic link to python to either of them. So on those platforms use -# an explicit Python version. -if(WIN32) - set(PYTHON_EXECUTABLE python) -else() - set(PYTHON_EXECUTABLE python3) -endif() - -execute_process( - COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/worker_codesign.py" - "${CMAKE_INSTALL_PREFIX}" - WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} - RESULT_VARIABLE exit_code -) - -if(NOT exit_code EQUAL "0") - message(FATAL_ERROR "Non-zero exit code of codesign tool") -endif() diff --git a/build_files/buildbot/worker_codesign.py b/build_files/buildbot/worker_codesign.py deleted file mode 100755 index a82ee98b1b5..00000000000 --- a/build_files/buildbot/worker_codesign.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# Helper script which takes care of signing provided location. -# -# The location can either be a directory (in which case all eligible binaries -# will be signed) or a single file (in which case a single file will be signed). -# -# This script takes care of all the complexity of communicating between process -# which requests file to be signed and the code signing server. -# -# NOTE: Signing happens in-place. - -import argparse -import sys - -from pathlib import Path - -from codesign.simple_code_signer import SimpleCodeSigner - - -def create_argument_parser(): - parser = argparse.ArgumentParser() - parser.add_argument('path_to_sign', type=Path) - return parser - - -def main(): - parser = create_argument_parser() - args = parser.parse_args() - path_to_sign = args.path_to_sign.absolute() - - if sys.platform == 'win32': - # When WIX packed is used to generate .msi on Windows the CPack will - # install two different projects and install them to different - # installation prefix: - # - # - C:\b\build\_CPack_Packages\WIX\Blender - # - C:\b\build\_CPack_Packages\WIX\Unspecified - # - # Annoying part is: CMake's post-install script will only be run - # once, with the install prefix which corresponds to a project which - # was installed last. But we want to sign binaries from all projects. - # So in order to do so we detect that we are running for a CPack's - # project used for WIX and force parent directory (which includes both - # projects) to be signed. - # - # Here we force both projects to be signed. - if path_to_sign.name == 'Unspecified' and 'WIX' in str(path_to_sign): - path_to_sign = path_to_sign.parent - - code_signer = SimpleCodeSigner() - code_signer.sign_file_or_directory(path_to_sign) - - -if __name__ == "__main__": - main() diff --git a/build_files/buildbot/worker_compile.py b/build_files/buildbot/worker_compile.py deleted file mode 100644 index 8c6b44c5866..00000000000 --- a/build_files/buildbot/worker_compile.py +++ /dev/null @@ -1,135 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -import os -import shutil - -import buildbot_utils - - -def get_cmake_options(builder): - codesign_script = os.path.join( - builder.blender_dir, 'build_files', 'buildbot', 'worker_codesign.cmake') - - config_file = "build_files/cmake/config/blender_release.cmake" - options = ['-DCMAKE_BUILD_TYPE:STRING=Release', - '-DWITH_GTESTS=ON'] - - if builder.platform == 'mac': - options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=x86_64') - options.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.9') - elif builder.platform == 'win': - options.extend(['-G', 'Visual Studio 16 2019', '-A', 'x64']) - if builder.codesign: - options.extend(['-DPOSTINSTALL_SCRIPT:PATH=' + codesign_script]) - elif builder.platform == 'linux': - config_file = "build_files/buildbot/config/blender_linux.cmake" - - optix_sdk_dir = os.path.join(builder.blender_dir, '..', '..', 'NVIDIA-Optix-SDK-7.1') - options.append('-DOPTIX_ROOT_DIR:PATH=' + optix_sdk_dir) - - # Workaround to build sm_30 kernels with CUDA 10, since CUDA 11 no longer supports that architecture - if builder.platform == 'win': - options.append('-DCUDA10_TOOLKIT_ROOT_DIR:PATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.1') - options.append('-DCUDA10_NVCC_EXECUTABLE:FILEPATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.1/bin/nvcc.exe') - options.append('-DCUDA11_TOOLKIT_ROOT_DIR:PATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.1') - options.append('-DCUDA11_NVCC_EXECUTABLE:FILEPATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.1/bin/nvcc.exe') - elif builder.platform == 'linux': - options.append('-DCUDA10_TOOLKIT_ROOT_DIR:PATH=/usr/local/cuda-10.1') - options.append('-DCUDA10_NVCC_EXECUTABLE:FILEPATH=/usr/local/cuda-10.1/bin/nvcc') - options.append('-DCUDA11_TOOLKIT_ROOT_DIR:PATH=/usr/local/cuda-11.1') - options.append('-DCUDA11_NVCC_EXECUTABLE:FILEPATH=/usr/local/cuda-11.1/bin/nvcc') - - options.append("-C" + os.path.join(builder.blender_dir, config_file)) - options.append("-DCMAKE_INSTALL_PREFIX=%s" % (builder.install_dir)) - - return options - - -def update_git(builder): - # Do extra git fetch because not all platform/git/buildbot combinations - # update the origin remote, causing buildinfo to detect local changes. - os.chdir(builder.blender_dir) - - print("Fetching remotes") - command = ['git', 'fetch', '--all'] - buildbot_utils.call(builder.command_prefix + command) - - -def clean_directories(builder): - # Make sure no garbage remained from the previous run - if os.path.isdir(builder.install_dir): - shutil.rmtree(builder.install_dir) - - # Make sure build directory exists and enter it - os.makedirs(builder.build_dir, exist_ok=True) - - # Remove buildinfo files to force buildbot to re-generate them. - for buildinfo in ('buildinfo.h', 'buildinfo.h.txt', ): - full_path = os.path.join(builder.build_dir, 'source', 'creator', buildinfo) - if os.path.exists(full_path): - print("Removing {}" . format(buildinfo)) - os.remove(full_path) - - -def cmake_configure(builder): - # CMake configuration - os.chdir(builder.build_dir) - - cmake_cache = os.path.join(builder.build_dir, 'CMakeCache.txt') - if os.path.exists(cmake_cache): - print("Removing CMake cache") - os.remove(cmake_cache) - - print("CMake configure:") - cmake_options = get_cmake_options(builder) - command = ['cmake', builder.blender_dir] + cmake_options - buildbot_utils.call(builder.command_prefix + command) - - -def cmake_build(builder): - # CMake build - os.chdir(builder.build_dir) - - # NOTE: CPack will build an INSTALL target, which would mean that code - # signing will happen twice when using `make install` and CPack. - # The tricky bit here is that it is not possible to know whether INSTALL - # target is used by CPack or by a buildbot itaself. Extra level on top of - # this is that on Windows it is required to build INSTALL target in order - # to have unit test binaries to run. - # So on the one hand we do an extra unneeded code sign on Windows, but on - # a positive side we don't add complexity and don't make build process more - # fragile trying to avoid this. The signing process is way faster than just - # a clean build of buildbot, especially with regression tests enabled. - if builder.platform == 'win': - command = ['cmake', '--build', '.', '--target', 'install', '--config', 'Release'] - else: - command = ['make', '-s', '-j16', 'install'] - - print("CMake build:") - buildbot_utils.call(builder.command_prefix + command) - - -if __name__ == "__main__": - builder = buildbot_utils.create_builder_from_arguments() - update_git(builder) - clean_directories(builder) - cmake_configure(builder) - cmake_build(builder) diff --git a/build_files/buildbot/worker_pack.py b/build_files/buildbot/worker_pack.py deleted file mode 100644 index a5727a66e09..00000000000 --- a/build_files/buildbot/worker_pack.py +++ /dev/null @@ -1,208 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -# Runs on buildbot worker, creating a release package using the build -# system and zipping it into buildbot_upload.zip. This is then uploaded -# to the master in the next buildbot step. - -import os -import sys - -from pathlib import Path - -import buildbot_utils - - -def get_package_name(builder, platform=None): - info = buildbot_utils.VersionInfo(builder) - - package_name = 'blender-' + info.full_version - if platform: - package_name += '-' + platform - if not (builder.branch == 'master' or builder.is_release_branch): - if info.is_development_build: - package_name = builder.branch + "-" + package_name - - return package_name - - -def sign_file_or_directory(path): - from codesign.simple_code_signer import SimpleCodeSigner - code_signer = SimpleCodeSigner() - code_signer.sign_file_or_directory(Path(path)) - - -def create_buildbot_upload_zip(builder, package_files): - import zipfile - - buildbot_upload_zip = os.path.join(builder.upload_dir, "buildbot_upload.zip") - if os.path.exists(buildbot_upload_zip): - os.remove(buildbot_upload_zip) - - try: - z = zipfile.ZipFile(buildbot_upload_zip, "w", compression=zipfile.ZIP_STORED) - for filepath, filename in package_files: - print("Packaged", filename) - z.write(filepath, arcname=filename) - z.close() - except Exception as ex: - sys.stderr.write('Create buildbot_upload.zip failed: ' + str(ex) + '\n') - sys.exit(1) - - -def create_tar_xz(src, dest, package_name): - # One extra to remove leading os.sep when cleaning root for package_root - ln = len(src) + 1 - flist = list() - - # Create list of tuples containing file and archive name - for root, dirs, files in os.walk(src): - package_root = os.path.join(package_name, root[ln:]) - flist.extend([(os.path.join(root, file), os.path.join(package_root, file)) for file in files]) - - import tarfile - - # Set UID/GID of archived files to 0, otherwise they'd be owned by whatever - # user compiled the package. If root then unpacks it to /usr/local/ you get - # a security issue. - def _fakeroot(tarinfo): - tarinfo.gid = 0 - tarinfo.gname = "root" - tarinfo.uid = 0 - tarinfo.uname = "root" - return tarinfo - - package = tarfile.open(dest, 'w:xz', preset=9) - for entry in flist: - package.add(entry[0], entry[1], recursive=False, filter=_fakeroot) - package.close() - - -def cleanup_files(dirpath, extension): - for f in os.listdir(dirpath): - filepath = os.path.join(dirpath, f) - if os.path.isfile(filepath) and f.endswith(extension): - os.remove(filepath) - - -def pack_mac(builder): - info = buildbot_utils.VersionInfo(builder) - - os.chdir(builder.build_dir) - cleanup_files(builder.build_dir, '.dmg') - - package_name = get_package_name(builder, 'macOS') - package_filename = package_name + '.dmg' - package_filepath = os.path.join(builder.build_dir, package_filename) - - release_dir = os.path.join(builder.blender_dir, 'release', 'darwin') - buildbot_dir = os.path.join(builder.blender_dir, 'build_files', 'buildbot') - bundle_script = os.path.join(buildbot_dir, 'worker_bundle_dmg.py') - - command = [bundle_script] - command += ['--dmg', package_filepath] - if info.is_development_build: - background_image = os.path.join(release_dir, 'buildbot', 'background.tif') - command += ['--background-image', background_image] - if builder.codesign: - command += ['--codesign'] - command += [builder.install_dir] - buildbot_utils.call(command) - - create_buildbot_upload_zip(builder, [(package_filepath, package_filename)]) - - -def pack_win(builder): - info = buildbot_utils.VersionInfo(builder) - - os.chdir(builder.build_dir) - cleanup_files(builder.build_dir, '.zip') - - # CPack will add the platform name - cpack_name = get_package_name(builder, None) - package_name = get_package_name(builder, 'windows' + str(builder.bits)) - - command = ['cmake', '-DCPACK_OVERRIDE_PACKAGENAME:STRING=' + cpack_name, '.'] - buildbot_utils.call(builder.command_prefix + command) - command = ['cpack', '-G', 'ZIP'] - buildbot_utils.call(builder.command_prefix + command) - - package_filename = package_name + '.zip' - package_filepath = os.path.join(builder.build_dir, package_filename) - package_files = [(package_filepath, package_filename)] - - if info.version_cycle == 'release': - # Installer only for final release builds, otherwise will get - # 'this product is already installed' messages. - command = ['cpack', '-G', 'WIX'] - buildbot_utils.call(builder.command_prefix + command) - - package_filename = package_name + '.msi' - package_filepath = os.path.join(builder.build_dir, package_filename) - if builder.codesign: - sign_file_or_directory(package_filepath) - - package_files += [(package_filepath, package_filename)] - - create_buildbot_upload_zip(builder, package_files) - - -def pack_linux(builder): - blender_executable = os.path.join(builder.install_dir, 'blender') - - info = buildbot_utils.VersionInfo(builder) - - # Strip all unused symbols from the binaries - print("Stripping binaries...") - buildbot_utils.call(builder.command_prefix + ['strip', '--strip-all', blender_executable]) - - print("Stripping python...") - py_target = os.path.join(builder.install_dir, info.short_version) - buildbot_utils.call( - builder.command_prefix + [ - 'find', py_target, '-iname', '*.so', '-exec', 'strip', '-s', '{}', ';', - ], - ) - - # Construct package name - platform_name = 'linux64' - package_name = get_package_name(builder, platform_name) - package_filename = package_name + ".tar.xz" - - print("Creating .tar.xz archive") - package_filepath = builder.install_dir + '.tar.xz' - create_tar_xz(builder.install_dir, package_filepath, package_name) - - # Create buildbot_upload.zip - create_buildbot_upload_zip(builder, [(package_filepath, package_filename)]) - - -if __name__ == "__main__": - builder = buildbot_utils.create_builder_from_arguments() - - # Make sure install directory always exists - os.makedirs(builder.install_dir, exist_ok=True) - - if builder.platform == 'mac': - pack_mac(builder) - elif builder.platform == 'win': - pack_win(builder) - elif builder.platform == 'linux': - pack_linux(builder) diff --git a/build_files/buildbot/worker_test.py b/build_files/buildbot/worker_test.py deleted file mode 100644 index 8a6e7d4bb69..00000000000 --- a/build_files/buildbot/worker_test.py +++ /dev/null @@ -1,42 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -import buildbot_utils -import os -import sys - - -def get_ctest_arguments(builder): - args = ['--output-on-failure'] - if builder.platform == 'win': - args += ['-C', 'Release'] - return args - - -def test(builder): - os.chdir(builder.build_dir) - - command = builder.command_prefix + ['ctest'] + get_ctest_arguments(builder) - buildbot_utils.call(command) - - -if __name__ == "__main__": - builder = buildbot_utils.create_builder_from_arguments() - test(builder) diff --git a/build_files/buildbot/worker_update.py b/build_files/buildbot/worker_update.py deleted file mode 100644 index 36a7ae31c84..00000000000 --- a/build_files/buildbot/worker_update.py +++ /dev/null @@ -1,31 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# - -import buildbot_utils -import os -import sys - -if __name__ == "__main__": - builder = buildbot_utils.create_builder_from_arguments() - os.chdir(builder.blender_dir) - - # Run make update which handles all libraries and submodules. - make_update = os.path.join(builder.blender_dir, "build_files", "utils", "make_update.py") - buildbot_utils.call([sys.executable, make_update, '--no-blender', "--use-tests", "--use-centos-libraries"]) -- cgit v1.2.3 From 63e50cf265d6f17785ffdf0d0a7220237b054050 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 27 May 2021 14:36:18 +0200 Subject: Fix T88499: Copy data path operator does not consider library affiliation When using the operator `ui.copy_data_path_button(full_path=True)` ({key ctrl shift Alt C} on hover) the copied path does not consider the library origin. That means that when there is a name clash the data path is not accurate and refers to the local item instead. This patch adds the library (if the ID is linked) of the returned string from RNA_path_full_ID_py. bpy.data.objects["Cube", "//library.blend"] instead of bpy.data.objects["Cube"] note: parsing this happens in pyrna_prop_collection_subscript_str_lib_pair_ptr Maniphest Tasks: T88499 Differential Revision: https://developer.blender.org/D11412 --- source/blender/makesrna/intern/rna_access.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 150a455f1c7..948fef1b51e 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -6115,13 +6115,25 @@ char *RNA_path_full_ID_py(Main *bmain, ID *id) path = ""; } - char id_esc[(sizeof(id->name) - 2) * 2]; + char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4]; + if (id->lib != NULL) { + int ofs = 0; + memcpy(lib_filepath_esc, ", \"", 3); + ofs += 3; + ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc)); + memcpy(lib_filepath_esc + ofs, "\"", 2); + } + else { + lib_filepath_esc[0] = '\0'; + } + char id_esc[(sizeof(id->name) - 2) * 2]; BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc)); - return BLI_sprintfN("bpy.data.%s[\"%s\"]%s%s", + return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s", BKE_idtype_idcode_to_name_plural(GS(id->name)), id_esc, + lib_filepath_esc, path[0] ? "." : "", path); } -- cgit v1.2.3 From 51bbdfbef3968b53a835d1a92c0622676453421a Mon Sep 17 00:00:00 2001 From: James Monteath Date: Fri, 28 May 2021 12:16:45 +0200 Subject: Moved to new git repo --- release/darwin/README.txt | 55 ---- release/darwin/blender.applescript | 18 -- release/darwin/bundle.sh | 212 -------------- release/freedesktop/snap/README.txt | 38 --- release/freedesktop/snap/bundle.py | 21 -- release/freedesktop/snap/snapcraft.yaml.in | 53 ---- release/steam/README.md | 70 ----- release/steam/blender_app_build.vdf.template | 17 -- release/steam/create_steam_builds.py | 397 --------------------------- release/steam/depot_build_linux.vdf.template | 31 --- release/steam/depot_build_macos.vdf.template | 30 -- release/steam/depot_build_win.vdf.template | 31 --- release/windows/msix/create_msix_package.py | 197 ------------- 13 files changed, 1170 deletions(-) delete mode 100644 release/darwin/README.txt delete mode 100644 release/darwin/blender.applescript delete mode 100755 release/darwin/bundle.sh delete mode 100644 release/freedesktop/snap/README.txt delete mode 100755 release/freedesktop/snap/bundle.py delete mode 100644 release/freedesktop/snap/snapcraft.yaml.in delete mode 100644 release/steam/README.md delete mode 100644 release/steam/blender_app_build.vdf.template delete mode 100644 release/steam/create_steam_builds.py delete mode 100644 release/steam/depot_build_linux.vdf.template delete mode 100644 release/steam/depot_build_macos.vdf.template delete mode 100644 release/steam/depot_build_win.vdf.template delete mode 100644 release/windows/msix/create_msix_package.py diff --git a/release/darwin/README.txt b/release/darwin/README.txt deleted file mode 100644 index 626ce8820ab..00000000000 --- a/release/darwin/README.txt +++ /dev/null @@ -1,55 +0,0 @@ - -macOS app bundling guide -======================== - -Install Code Signing Certificate --------------------------------- - -* Go to https://developer.apple.com/account/resources/certificates/list -* Download the Developer ID Application certificate. -* Double click the file and add to key chain (default options). -* Delete the file from the Downloads folder. - -* You will also need to install a .p12 public/private key file for the - certificate. This is only available for the owner of the Blender account, - or can be exported and copied from another system that already has code - signing set up. - -Find the codesigning identity by running: - -$ security find-identity -v -p codesigning - -"Developer ID Application: Stichting Blender Foundation" is the identity needed. -The long code at the start of the line is used as below. - -Setup Apple ID --------------- - -* The Apple ID must have two step verification enabled. -* Create an app specific password for the code signing app (label can be anything): -https://support.apple.com/en-us/HT204397 -* Add the app specific password to keychain: - -$ security add-generic-password -a -w -s altool-password - -When running the bundle script, there will be a popup. To avoid that either: -* Click Always Allow in the popup -* In the Keychain Access app, change the Access Control settings on altool-password - -Bundle ------- - -Then the bundle is created as follows: - -$ ./bundle.sh --source --dmg --bundle-id --username --password "@keychain:altool-password" --codesign - - directory where built Blender.app is - location and name of the final disk image - id on notarization, for example org.blenderfoundation.blender.release - your appleid email - codesigning identity - -When specifying only --sourcedir and --dmg, the build will not be signed. - -Example : -$ ./bundle.sh --source /data/build/bin --dmg /data/Blender-2.8-alpha-macOS-10.11.dmg --bundle-id org.blenderfoundation.blender.release --username "foo@mac.com" --password "@keychain:altool-password" --codesign AE825E26F12D08B692F360133210AF46F4CF7B97 diff --git a/release/darwin/blender.applescript b/release/darwin/blender.applescript deleted file mode 100644 index 130dc2eb64c..00000000000 --- a/release/darwin/blender.applescript +++ /dev/null @@ -1,18 +0,0 @@ -tell application "Finder" - tell disk "Blender" - open - set current view of container window to icon view - set toolbar visible of container window to false - set statusbar visible of container window to false - set the bounds of container window to {100, 100, 640, 472} - set theViewOptions to icon view options of container window - set arrangement of theViewOptions to not arranged - set icon size of theViewOptions to 128 - set background picture of theViewOptions to file ".background:background.tif" - set position of item " " of container window to {400, 190} - set position of item "blender.app" of container window to {135, 190} - update without registering applications - delay 5 - close - end tell -end tell diff --git a/release/darwin/bundle.sh b/release/darwin/bundle.sh deleted file mode 100755 index 6d8695a441d..00000000000 --- a/release/darwin/bundle.sh +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env bash -# -# Script to create a macOS dmg file for Blender builds, including code -# signing and notarization for releases. - -# Check that we have all needed tools. -for i in osascript git codesign hdiutil xcrun ; do - if [ ! -x "$(which ${i})" ]; then - echo "Unable to execute command $i, macOS broken?" - exit 1 - fi -done - -# Defaults settings. -_script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -_volume_name="Blender" -_tmp_dir="$(mktemp -d)" -_tmp_dmg="/tmp/blender-tmp.dmg" -_background_image="${_script_dir}/background.tif" -_mount_dir="/Volumes/${_volume_name}" -_entitlements="${_script_dir}/entitlements.plist" - -# Handle arguments. -while [[ $# -gt 0 ]]; do - key=$1 - case $key in - -s|--source) - SRC_DIR="$2" - shift - shift - ;; - -d|--dmg) - DEST_DMG="$2" - shift - shift - ;; - -b|--bundle-id) - N_BUNDLE_ID="$2" - shift - shift - ;; - -u|--username) - N_USERNAME="$2" - shift - shift - ;; - -p|--password) - N_PASSWORD="$2" - shift - shift - ;; - -c|--codesign) - C_CERT="$2" - shift - shift - ;; - --background-image) - _background_image="$2" - shift - shift - ;; - -h|--help) - echo "Usage:" - echo " $(basename "$0") --source DIR --dmg IMAGENAME " - echo " optional arguments:" - echo " --codesign " - echo " --username " - echo " --password " - echo " --bundle-id " - echo " Check https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/customizing_the_notarization_workflow " - exit 1 - ;; - esac -done - -if [ ! -d "${SRC_DIR}/Blender.app" ]; then - echo "use --source parameter to set source directory where Blender.app can be found" - exit 1 -fi - -if [ -z "${DEST_DMG}" ]; then - echo "use --dmg parameter to set output dmg name" - exit 1 -fi - -# Destroy destination dmg if there is any. -test -f "${DEST_DMG}" && rm "${DEST_DMG}" -if [ -d "${_mount_dir}" ]; then - echo -n "Ejecting existing blender volume.." - DEV_FILE=$(mount | grep "${_mount_dir}" | awk '{ print $1 }') - diskutil eject "${DEV_FILE}" || exit 1 - echo -fi - -# Copy dmg contents. -echo -n "Copying Blender.app..." -cp -r "${SRC_DIR}/Blender.app" "${_tmp_dir}/" || exit 1 -echo - -# Create the disk image. -_directory_size=$(du -sh ${_tmp_dir} | awk -F'[^0-9]*' '$0=$1') -_image_size=$(echo "${_directory_size}" + 400 | bc) # extra 400 need for codesign to work (why on earth?) - -echo -echo -n "Creating disk image of size ${_image_size}M.." -test -f "${_tmp_dmg}" && rm "${_tmp_dmg}" -hdiutil create -size "${_image_size}m" -fs HFS+ -srcfolder "${_tmp_dir}" -volname "${_volume_name}" -format UDRW "${_tmp_dmg}" -mode 755 - -echo "Mounting readwrite image..." -hdiutil attach -readwrite -noverify -noautoopen "${_tmp_dmg}" - -echo "Setting background picture.." -if ! test -z "${_background_image}"; then - echo "Copying background image ..." - test -d "${_mount_dir}/.background" || mkdir "${_mount_dir}/.background" - _background_image_NAME=$(basename "${_background_image}") - cp "${_background_image}" "${_mount_dir}/.background/${_background_image_NAME}" -fi - -echo "Creating link to /Applications ..." -ln -s /Applications "${_mount_dir}/Applications" -echo "Renaming Applications to empty string." -mv ${_mount_dir}/Applications "${_mount_dir}/ " - -echo "Running applescript to set folder looks ..." -cat "${_script_dir}/blender.applescript" | osascript - -echo "Waiting after applescript ..." -sleep 5 - -if [ ! -z "${C_CERT}" ]; then - # Codesigning requires all libs and binaries to be signed separately. - echo -n "Codesigning Python" - for f in $(find "${_mount_dir}/Blender.app/Contents/Resources" -name "python*"); do - if [ -x ${f} ] && [ ! -d ${f} ]; then - codesign --remove-signature "${f}" - codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${f}" - fi - done - echo ; echo -n "Codesigning .dylib and .so libraries" - for f in $(find "${_mount_dir}/Blender.app" -name "*.dylib" -o -name "*.so"); do - codesign --remove-signature "${f}" - codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${f}" - done - echo ; echo -n "Codesigning Blender.app" - codesign --remove-signature "${_mount_dir}/Blender.app" - codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${_mount_dir}/Blender.app" - echo -else - echo "No codesigning cert given, skipping..." -fi - -# Need to eject dev files to remove /dev files and free .dmg for converting -echo "Unmounting rw disk image ..." -DEV_FILE=$(mount | grep "${_mount_dir}" | awk '{ print $1 }') -diskutil eject "${DEV_FILE}" - -sleep 3 - -echo "Compressing disk image ..." -hdiutil convert "${_tmp_dmg}" -format UDZO -o "${DEST_DMG}" - -# Codesign the dmg -if [ ! -z "${C_CERT}" ]; then - echo -n "Codesigning dmg..." - codesign --timestamp --force --sign "${C_CERT}" "${DEST_DMG}" - echo -fi - -# Cleanup -rm -rf "${_tmp_dir}" -rm "${_tmp_dmg}" - -# Notarize -if [ ! -z "${N_USERNAME}" ] && [ ! -z "${N_PASSWORD}" ] && [ ! -z "${N_BUNDLE_ID}" ]; then - # Send to Apple - echo "Sending ${DEST_DMG} for notarization..." - _tmpout=$(mktemp) - echo xcrun altool --notarize-app --verbose -f "${DEST_DMG}" --primary-bundle-id "${N_BUNDLE_ID}" --username "${N_USERNAME}" --password "${N_PASSWORD}" - xcrun altool --notarize-app --verbose -f "${DEST_DMG}" --primary-bundle-id "${N_BUNDLE_ID}" --username "${N_USERNAME}" --password "${N_PASSWORD}" >${_tmpout} 2>&1 - - # Parse request uuid - _requuid=$(cat "${_tmpout}" | grep "RequestUUID" | awk '{ print $3 }') - echo "RequestUUID: ${_requuid}" - if [ ! -z "${_requuid}" ]; then - # Wait for Apple to confirm notarization is complete - echo "Waiting for notarization to be complete.." - for c in {20..0};do - sleep 600 - xcrun altool --notarization-info "${_requuid}" --username "${N_USERNAME}" --password "${N_PASSWORD}" >${_tmpout} 2>&1 - _status=$(cat "${_tmpout}" | grep "Status:" | awk '{ print $2 }') - if [ "${_status}" == "invalid" ]; then - echo "Got invalid notarization!" - break; - fi - - if [ "${_status}" == "success" ]; then - echo -n "Notarization successful! Stapling..." - xcrun stapler staple -v "${DEST_DMG}" - break; - fi - echo "Notarization in progress, waiting..." - done - else - cat ${_tmpout} - echo "Error getting RequestUUID, notarization unsuccessful" - fi -else - echo "No notarization credentials supplied, skipping..." -fi - -echo "..done. You should have ${DEST_DMG} ready to upload" diff --git a/release/freedesktop/snap/README.txt b/release/freedesktop/snap/README.txt deleted file mode 100644 index 2e8822f32dc..00000000000 --- a/release/freedesktop/snap/README.txt +++ /dev/null @@ -1,38 +0,0 @@ - -Snap Package Instructions -========================= - -This folder contains the scripts for creating and uploading the snap on: -https://snapcraft.io/blender - - -Setup ------ - -This has only been tested to work on Ubuntu. - -# Install required packages -sudo apt install snapd snapcraft - - -Steps ------ - -# Build the snap file -python3 bundle.py --version 2.XX --url https://download.blender.org/release/Blender2.XX/blender-2.XX-x86_64.tar.bz2 - -# Install snap to test -# --dangerous is needed since the snap has not been signed yet -# --classic is required for installing Blender in general -sudo snap install --dangerous --classic blender_2.XX_amd64.snap - -# Upload -snapcraft push --release=stable blender_2.XX_amd64.snap - - -Release Values --------------- - -stable: final release -candidate: release candidates - diff --git a/release/freedesktop/snap/bundle.py b/release/freedesktop/snap/bundle.py deleted file mode 100755 index c3ecc5af561..00000000000 --- a/release/freedesktop/snap/bundle.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import pathlib -import subprocess - -parser = argparse.ArgumentParser() -parser.add_argument("--version", required=True) -parser.add_argument("--url", required=True) -parser.add_argument("--grade", default="stable", choices=["stable", "devel"]) -args = parser.parse_args() - -yaml_text = pathlib.Path("snapcraft.yaml.in").read_text() -yaml_text = yaml_text.replace("@VERSION@", args.version) -yaml_text = yaml_text.replace("@URL@", args.url) -yaml_text = yaml_text.replace("@GRADE@", args.grade) -pathlib.Path("snapcraft.yaml").write_text(yaml_text) - -subprocess.call(["snapcraft", "clean"]) -subprocess.call(["snapcraft", "snap"]) diff --git a/release/freedesktop/snap/snapcraft.yaml.in b/release/freedesktop/snap/snapcraft.yaml.in deleted file mode 100644 index eb3ef97eba8..00000000000 --- a/release/freedesktop/snap/snapcraft.yaml.in +++ /dev/null @@ -1,53 +0,0 @@ -name: blender -summary: Blender is the free and open source 3D creation suite. -description: | - Blender is the free and open source 3D creation suite. It supports the - entirety of the 3D pipeline—modeling, rigging, animation, simulation, - rendering, compositing and motion tracking, and video editing. - - Blender is a public project, made by hundreds of people from around the - world; by studios and individual artists, professionals and hobbyists, - scientists, students, VFX experts, animators, game artists, modders, and - the list goes on. - - The standard snap channels are used in the following way: - - stable - Latest stable release. - candidate - Test builds for the upcoming stable release. - -icon: ../icons/scalable/apps/blender.svg - -passthrough: - license: GPL-3.0 - -confinement: classic - -apps: - blender: - command: ./blender-wrapper - desktop: ./blender.desktop - -version: '@VERSION@' -grade: @GRADE@ - -parts: - blender: - plugin: dump - source: @URL@ - build-attributes: [keep-execstack, no-patchelf] - override-build: | - snapcraftctl build - sed -i 's|Icon=blender|Icon=${SNAP}/blender.svg|' ${SNAPCRAFT_PART_INSTALL}/blender.desktop - stage-packages: - - libxcb1 - - libxext6 - - libx11-6 - - libxi6 - - libxfixes3 - - libxrender1 - - libxxf86vm1 - wrapper: - plugin: copy - source: . - files: - blender-wrapper: blender-wrapper diff --git a/release/steam/README.md b/release/steam/README.md deleted file mode 100644 index 05eda799c3f..00000000000 --- a/release/steam/README.md +++ /dev/null @@ -1,70 +0,0 @@ -Creating Steam builds for Blender -================================= - -This script automates creation of the Steam files: download of the archives, -extraction of the archives, preparation of the build scripts (VDF files), actual -building of the Steam game files. - -Requirements -============ - -* MacOS machine - Tested on Catalina 10.15.6. Extracting contents from the DMG - archive did not work Windows nor on Linux using 7-zip. All DMG archives tested - failed to be extracted. As such only MacOS is known to work. -* Steam SDK downloaded from SteamWorks - The `steamcmd` is used to generate the - Steam game files. The path to the `steamcmd` is what is actually needed. -* SteamWorks credentials - Needed to log in using `steamcmd`. -* Login to SteamWorks with the `steamcmd` from the command-line at least once - - Needded to ensure the user is properly logged in. On a new machine the user - will have to go through two-factor authentication. -* App ID and Depot IDs - Needed to create the VDF files. -* Python 3.x - 3.7 was tested. -* Base URL - for downloading the archives. - -Usage -===== - -```bash -$ export STEAMUSER=SteamUserName -$ export STEAMPW=SteamUserPW -$ export BASEURL=https://download.blender.org/release/Blender2.83/ -$ export VERSION=2.83.3 -$ export APPID=appidnr -$ export WINID=winidnr -$ export LINID=linuxidnr -$ export MACOSID=macosidnr - -# log in to SteamWorks from command-line at least once - -$ ../sdk/tools/ContentBuilder/builder_osx/steamcmd +login $STEAMUSER $STEAMPW - -# once that has been done we can now actually start our tool - -$ python3.7 create_steam_builds.py --baseurl $BASEURL --version $VERSION --appid $APPID --winid $WINID --linuxid $LINID --macosid $MACOSID --steamuser $STEAMUSER --steampw $STEAMPW --steamcmd ../sdk/tools/ContentBuilder/builder_osx/steamcmd -``` - -All arguments in the above example are required. - -At the start the tool will login using `steamcmd`. This is necessary to let the -Steam SDK update itself if necessary. - -There are a few optional arguments: - -* `--dryrun`: If set building the game files will not actually happen. A set of - log files and a preview manifest per depot will be created in the output folder. - This can be used to double-check everything works as expected. -* `--skipdl`: If set will skip downloading of the archives. The tool expects the - archives to already exist in the correct content location. -* `--skipextract`: If set will skip extraction of all archives. The tool expects - the archives to already have been correctly extracted in the content location. - -Run the tool with `-h` for detailed information on each argument. - -The content and output folders are generated through appending the version -without dots to the words `content` and `output` respectively, e.g. `content2833` -and `output2833`. These folders are created next to the tool. - -From all `.template` files the Steam build scripts will be generated also in the -same directory as the tool. The files will have the extension `.vdf`. - -In case of errors the tool will have a non-zero return code. \ No newline at end of file diff --git a/release/steam/blender_app_build.vdf.template b/release/steam/blender_app_build.vdf.template deleted file mode 100644 index 9e2d0625d72..00000000000 --- a/release/steam/blender_app_build.vdf.template +++ /dev/null @@ -1,17 +0,0 @@ -"appbuild" -{ - "appid" "[APPID]" - "desc" "Blender [VERSION]" // description for this build - "buildoutput" "./[OUTPUT]" // build output folder for .log, .csm & .csd files, relative to location of this file - "contentroot" "./[CONTENT]" // root content folder, relative to location of this file - "setlive" "" // branch to set live after successful build, non if empty - "preview" "[DRYRUN]" // 1 to enable preview builds, 0 to commit build to steampipe - "local" "" // set to flie path of local content server - - "depots" - { - "[WINID]" "depot_build_win.vdf" - "[LINUXID]" "depot_build_linux.vdf" - "[MACOSID]" "depot_build_macos.vdf" - } -} diff --git a/release/steam/create_steam_builds.py b/release/steam/create_steam_builds.py deleted file mode 100644 index 2ecd0c347f7..00000000000 --- a/release/steam/create_steam_builds.py +++ /dev/null @@ -1,397 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import pathlib -import requests -import shutil -import subprocess -from typing import Callable, Iterator, List, Tuple - -# supported archive and platform endings, used to create actual archive names -archive_endings = ["windows64.zip", "linux64.tar.xz", "macOS.dmg"] - - -def add_optional_argument(option: str, help: str) -> None: - global parser - """Add an optional argument - - Args: - option (str): Option to add - help (str): Help description for the argument - """ - parser.add_argument(option, help=help, action='store_const', const=1) - - -def blender_archives(version: str) -> Iterator[str]: - """Generator for Blender archives for version. - - Yields for items in archive_endings an archive name in the form of - blender-{version}-{ending}. - - Args: - version (str): Version string of the form 2.83.2 - - - Yields: - Iterator[str]: Name in the form of blender-{version}-{ending} - """ - global archive_endings - - for ending in archive_endings: - yield f"blender-{version}-{ending}" - - -def get_archive_type(archive_type: str, version: str) -> str: - """Return the archive of given type and version. - - Args: - archive_type (str): extension for archive type to check for - version (str): Version string in the form 2.83.2 - - Raises: - Exception: Execption when archive type isn't found - - Returns: - str: archive name for given type - """ - - for archive in blender_archives(version): - if archive.endswith(archive_type): - return archive - raise Exception("Unknown archive type") - - -def execute_command(cmd: List[str], name: str, errcode: int, cwd=".", capture_output=True) -> str: - """Execute the given command. - - Returns the process stdout upon success if any. - - On error print message the command with name that has failed. Print stdout - and stderr of the process if any, and then exit with given error code. - - Args: - cmd (List[str]): Command in list format, each argument as their own item - name (str): Name of command to use when printing to command-line - errcode (int): Error code to use in case of exit() - cwd (str, optional): Folder to use as current work directory for command - execution. Defaults to ".". - capture_output (bool, optional): Whether to capture command output or not. - Defaults to True. - - Returns: - str: stdout if any, or empty string - """ - cmd_process = subprocess.run( - cmd, capture_output=capture_output, encoding="UTF-8", cwd=cwd) - if cmd_process.returncode == 0: - if cmd_process.stdout: - return cmd_process.stdout - else: - return "" - else: - print(f"ERROR: {name} failed.") - if cmd_process.stdout: - print(cmd_process.stdout) - if cmd_process.stderr: - print(cmd_process.stderr) - exit(errcode) - return "" - - -def download_archives(base_url: str, archives: Callable[[str], Iterator[str]], version: str, dst_dir: pathlib.Path): - """Download archives from the given base_url. - - Archives is a generator for Blender archive names based on version. - - Archive names are appended to the base_url to load from, and appended to - dst_dir to save to. - - Args: - base_url (str): Base URL to load archives from - archives (Callable[[str], Iterator[str]]): Generator for Blender archive - names based on version - version (str): Version string in the form of 2.83.2 - dst_dir (pathlib.Path): Download destination - """ - - if base_url[-1] != '/': - base_url = base_url + '/' - - for archive in archives(version): - download_url = f"{base_url}{archive}" - target_file = dst_dir.joinpath(archive) - download_file(download_url, target_file) - - -def download_file(from_url: str, to_file: pathlib.Path) -> None: - """Download from_url as to_file. - - Actual downloading will be skipped if --skipdl is given on the command-line. - - Args: - from_url (str): Full URL to resource to download - to_file (pathlib.Path): Full path to save downloaded resource as - """ - global args - - if not args.skipdl or not to_file.exists(): - print(f"Downloading {from_url}") - with open(to_file, "wb") as download_zip: - response = requests.get(from_url) - if response.status_code != requests.codes.ok: - print(f"ERROR: failed to download {from_url} (status code: {response.status_code})") - exit(1313) - download_zip.write(response.content) - else: - print(f"Downloading {from_url} skipped") - print(" ... OK") - - -def copy_contents_from_dmg_to_path(dmg_file: pathlib.Path, dst: pathlib.Path) -> None: - """Copy the contents of the given DMG file to the destination folder. - - Args: - dmg_file (pathlib.Path): Full path to DMG archive to extract from - dst (pathlib.Path): Full path to destination to extract to - """ - hdiutil_attach = ["hdiutil", - "attach", - "-readonly", - f"{dmg_file}" - ] - attached = execute_command(hdiutil_attach, "hdiutil attach", 1) - - # Last line of output is what we want, it is of the form - # /dev/somedisk Apple_HFS /Volumes/Blender - # We want to retain the mount point, and the folder the mount is - # created on. The mounted disk we need for detaching, the folder we - # need to be able to copy the contents to where we can use them - attachment_items = attached.splitlines()[-1].split() - mounted_disk = attachment_items[0] - source_location = pathlib.Path(attachment_items[2], "Blender.app") - - print(f"{source_location} -> {dst}") - - shutil.copytree(source_location, dst) - - hdiutil_detach = ["hdiutil", - "detach", - f"{mounted_disk}" - ] - execute_command(hdiutil_detach, "hdiutil detach", 2) - - -def create_build_script(template_name: str, vars: List[Tuple[str, str]]) -> pathlib.Path: - """ - Create the Steam build script - - Use the given template and template variable tuple list. - - Returns pathlib.Path to the created script. - - Args: - template_name (str): [description] - vars (List[Tuple[str, str]]): [description] - - Returns: - pathlib.Path: Full path to the generated script - """ - build_script = pathlib.Path(".", template_name).read_text() - for var in vars: - build_script = build_script.replace(var[0], var[1]) - build_script_file = template_name.replace(".template", "") - build_script_path = pathlib.Path(".", build_script_file) - build_script_path.write_text(build_script) - return build_script_path - - -def clean_up() -> None: - """Remove intermediate files depending on given command-line arguments - """ - global content_location, args - - if not args.leavearch and not args.leaveextracted: - shutil.rmtree(content_location) - - if args.leavearch and not args.leaveextracted: - shutil.rmtree(content_location.joinpath(zip_extract_folder)) - shutil.rmtree(content_location.joinpath(tarxz_extract_folder)) - shutil.rmtree(content_location.joinpath(dmg_extract_folder)) - - if args.leaveextracted and not args.leavearch: - import os - os.remove(content_location.joinpath(zipped_blender)) - os.remove(content_location.joinpath(tarxz_blender)) - os.remove(content_location.joinpath(dmg_blender)) - - -def extract_archive(archive: str, extract_folder_name: str, - cmd: List[str], errcode: int) -> None: - """Extract all files from archive to given folder name. - - Will not extract if - target folder already exists, or if --skipextract was given on the - command-line. - - Args: - archive (str): Archive name to extract - extract_folder_name (str): Folder name to extract to - cmd (List[str]): Command with arguments to use - errcode (int): Error code to use for exit() - """ - global args, content_location - - extract_location = content_location.joinpath(extract_folder_name) - - pre_extract = set(content_location.glob("*")) - - if not args.skipextract or not extract_location.exists(): - print(f"Extracting files from {archive}...") - cmd.append(content_location.joinpath(archive)) - execute_command(cmd, cmd[0], errcode, cwd=content_location) - # in case we use a non-release archive the naming will be incorrect. - # simply rename to expected target name - post_extract = set(content_location.glob("*")) - diff_extract = post_extract - pre_extract - if not extract_location in diff_extract: - folder_to_rename = list(diff_extract)[0] - folder_to_rename.rename(extract_location) - print(" OK") - else: - print(f"Skipping extraction {archive}!") - -# ============================================================================== - - -parser = argparse.ArgumentParser() - -parser.add_argument("--baseurl", required=True, - help="The base URL for files to download, " - "i.e. https://download.blender.org/release/Blender2.83/") - -parser.add_argument("--version", required=True, - help="The Blender version to release, in the form 2.83.3") - -parser.add_argument("--appid", required=True, - help="The Blender App ID on Steam") -parser.add_argument("--winid", required=True, - help="The Windows depot ID") -parser.add_argument("--linuxid", required=True, - help="The Linux depot ID") -parser.add_argument("--macosid", required=True, - help="The MacOS depot ID") - -parser.add_argument("--steamcmd", required=True, - help="Path to the steamcmd") -parser.add_argument("--steamuser", required=True, - help="The login for the Steam builder user") -parser.add_argument("--steampw", required=True, - help="Login password for the Steam builder user") - -add_optional_argument("--dryrun", - "If set the Steam files will not be uploaded") -add_optional_argument("--leavearch", - help="If set don't clean up the downloaded archives") -add_optional_argument("--leaveextracted", - help="If set don't clean up the extraction folders") -add_optional_argument("--skipdl", - help="If set downloading the archives is skipped if it already exists locally.") -add_optional_argument("--skipextract", - help="If set skips extracting of archives. The tool assumes the archives" - "have already been extracted to their correct locations") - -args = parser.parse_args() - -VERSIONNODOTS = args.version.replace('.', '') -OUTPUT = f"output{VERSIONNODOTS}" -CONTENT = f"content{VERSIONNODOTS}" - -# ===== set up main locations - -content_location = pathlib.Path(".", CONTENT).absolute() -output_location = pathlib.Path(".", OUTPUT).absolute() - -content_location.mkdir(parents=True, exist_ok=True) -output_location.mkdir(parents=True, exist_ok=True) - -# ===== login - -# Logging into Steam once to ensure the SDK updates itself properly. If we don't -# do that the combined +login and +run_app_build_http at the end of the tool -# will fail. -steam_login = [args.steamcmd, - "+login", - args.steamuser, - args.steampw, - "+quit" - ] -print("Logging in to Steam...") -execute_command(steam_login, "Login to Steam", 10) -print(" OK") - -# ===== prepare Steam build scripts - -template_vars = [ - ("[APPID]", args.appid), - ("[OUTPUT]", OUTPUT), - ("[CONTENT]", CONTENT), - ("[VERSION]", args.version), - ("[WINID]", args.winid), - ("[LINUXID]", args.linuxid), - ("[MACOSID]", args.macosid), - ("[DRYRUN]", f"{args.dryrun}" if args.dryrun else "0") -] - -blender_app_build = create_build_script( - "blender_app_build.vdf.template", template_vars) -create_build_script("depot_build_win.vdf.template", template_vars) -create_build_script("depot_build_linux.vdf.template", template_vars) -create_build_script("depot_build_macos.vdf.template", template_vars) - -# ===== download archives - -download_archives(args.baseurl, blender_archives, - args.version, content_location) - -# ===== set up file and folder names - -zipped_blender = get_archive_type("zip", args.version) -zip_extract_folder = zipped_blender.replace(".zip", "") -tarxz_blender = get_archive_type("tar.xz", args.version) -tarxz_extract_folder = tarxz_blender.replace(".tar.xz", "") -dmg_blender = get_archive_type("dmg", args.version) -dmg_extract_folder = dmg_blender.replace(".dmg", "") - -# ===== extract - -unzip_cmd = ["unzip", "-q"] -extract_archive(zipped_blender, zip_extract_folder, unzip_cmd, 3) - -untarxz_cmd = ["tar", "-xf"] -extract_archive(tarxz_blender, tarxz_extract_folder, untarxz_cmd, 4) - -if not args.skipextract or not content_location.joinpath(dmg_extract_folder).exists(): - print("Extracting files from Blender MacOS archive...") - blender_dmg = content_location.joinpath(dmg_blender) - target_location = content_location.joinpath( - dmg_extract_folder, "Blender.app") - copy_contents_from_dmg_to_path(blender_dmg, target_location) - print(" OK") -else: - print("Skipping extraction of .dmg!") - -# ===== building - -print("Build Steam game files...") -steam_build = [args.steamcmd, - "+login", - args.steamuser, - args.steampw, - "+run_app_build_http", - blender_app_build.absolute(), - "+quit" - ] -execute_command(steam_build, "Build with steamcmd", 13) -print(" OK") - -clean_up() diff --git a/release/steam/depot_build_linux.vdf.template b/release/steam/depot_build_linux.vdf.template deleted file mode 100644 index 0f69008548e..00000000000 --- a/release/steam/depot_build_linux.vdf.template +++ /dev/null @@ -1,31 +0,0 @@ -"DepotBuildConfig" -{ - // Set your assigned depot ID here - "DepotID" "[LINUXID]" - - // Set a root for all content. - // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) - // will be resolved relative to this root. - // If you don't define ContentRoot, then it will be assumed to be - // the location of this script file, which probably isn't what you want - "ContentRoot" "./blender-[VERSION]-linux64/" - - // include all files recursivley - "FileMapping" - { - // This can be a full path, or a path relative to ContentRoot - "LocalPath" "*" - - // This is a path relative to the install folder of your game - "DepotPath" "." - - // If LocalPath contains wildcards, setting this means that all - // matching files within subdirectories of LocalPath will also - // be included. - "recursive" "1" - } - - // but exclude all symbol files - // This can be a full path, or a path relative to ContentRoot - "FileExclusion" "*.pdb" -} diff --git a/release/steam/depot_build_macos.vdf.template b/release/steam/depot_build_macos.vdf.template deleted file mode 100644 index 33dde860462..00000000000 --- a/release/steam/depot_build_macos.vdf.template +++ /dev/null @@ -1,30 +0,0 @@ -"DepotBuildConfig" -{ - // Set your assigned depot ID here - "DepotID" "[MACOSID]" - - // Set a root for all content. - // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) - // will be resolved relative to this root. - // If you don't define ContentRoot, then it will be assumed to be - // the location of this script file, which probably isn't what you want - "ContentRoot" "./blender-[VERSION]-macOS/" - // include all files recursivley - "FileMapping" - { - // This can be a full path, or a path relative to ContentRoot - "LocalPath" "*" - - // This is a path relative to the install folder of your game - "DepotPath" "." - - // If LocalPath contains wildcards, setting this means that all - // matching files within subdirectories of LocalPath will also - // be included. - "recursive" "1" - } - - // but exclude all symbol files - // This can be a full path, or a path relative to ContentRoot - "FileExclusion" "*.pdb" -} diff --git a/release/steam/depot_build_win.vdf.template b/release/steam/depot_build_win.vdf.template deleted file mode 100644 index 2c18a0f15dd..00000000000 --- a/release/steam/depot_build_win.vdf.template +++ /dev/null @@ -1,31 +0,0 @@ -"DepotBuildConfig" -{ - // Set your assigned depot ID here - "DepotID" "[WINID]" - - // Set a root for all content. - // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) - // will be resolved relative to this root. - // If you don't define ContentRoot, then it will be assumed to be - // the location of this script file, which probably isn't what you want - "ContentRoot" "./blender-[VERSION]-windows64/" - - // include all files recursivley - "FileMapping" - { - // This can be a full path, or a path relative to ContentRoot - "LocalPath" "*" - - // This is a path relative to the install folder of your game - "DepotPath" "." - - // If LocalPath contains wildcards, setting this means that all - // matching files within subdirectories of LocalPath will also - // be included. - "recursive" "1" - } - - // but exclude all symbol files - // This can be a full path, or a path relative to ContentRoot - "FileExclusion" "*.pdb" -} diff --git a/release/windows/msix/create_msix_package.py b/release/windows/msix/create_msix_package.py deleted file mode 100644 index 3e41484eef5..00000000000 --- a/release/windows/msix/create_msix_package.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import pathlib -import requests -import shutil -import subprocess -import zipfile - -parser = argparse.ArgumentParser() -parser.add_argument( - "--version", - required=True, - help="Version string in the form of 2.83.3.0", -) -parser.add_argument( - "--url", - required=True, - help="Location of the release ZIP archive to download", -) -parser.add_argument( - "--publisher", - required=True, - help="A string in the form of 'CN=PUBLISHER'", -) -parser.add_argument( - "--pfx", - required=False, - help="Absolute path to the PFX file used for signing the resulting MSIX package", -) -parser.add_argument( - "--password", - required=False, - default="blender", - help="Password for the PFX file", -) -parser.add_argument( - "--lts", - required=False, - help="If set this MSIX is for an LTS release", - action='store_const', - const=1, -) -parser.add_argument( - "--skipdl", - required=False, - help="If set skip downloading of the specified URL as blender.zip. The tool assumes blender.zip exists", - action='store_const', - const=1, -) -parser.add_argument( - "--leavezip", - required=False, - help="If set don't clean up the downloaded blender.zip", - action='store_const', - const=1, -) -parser.add_argument( - "--overwrite", - required=False, - help="If set remove Content folder if it already exists", - action='store_const', - const=1, -) -args = parser.parse_args() - - -def execute_command(cmd: list, name: str, errcode: int): - """ - Execute given command in cmd. Output is captured. If an error - occurs name is used to print ERROR message, along with stderr and - stdout of the process if either was captured. - """ - cmd_process = subprocess.run(cmd, capture_output=True, encoding="UTF-8") - if cmd_process.returncode != 0: - print(f"ERROR: {name} failed.") - if cmd_process.stdout: - print(cmd_process.stdout) - if cmd_process.stderr: - print(cmd_process.stderr) - exit(errcode) - - -LTSORNOT = "" -PACKAGETYPE = "" -if args.lts: - versionparts = args.version.split(".") - LTSORNOT = f" {versionparts[0]}.{versionparts[1]} LTS" - PACKAGETYPE = f"{versionparts[0]}.{versionparts[1]}LTS" - -blender_package_msix = pathlib.Path(".", f"blender-{args.version}-windows64.msix").absolute() -content_folder = pathlib.Path(".", "Content") -content_blender_folder = pathlib.Path(content_folder, "Blender").absolute() -content_assets_folder = pathlib.Path(content_folder, "Assets") -assets_original_folder = pathlib.Path(".", "Assets") - -pri_config_file = pathlib.Path(".", "priconfig.xml") -pri_resources_file = pathlib.Path(content_folder, "resources.pri") - -local_blender_zip = pathlib.Path(".", "blender.zip") - -if args.pfx: - pfx_path = pathlib.Path(args.pfx) - if not pfx_path.exists(): - print("ERROR: PFX file not found. Please ensure you give the correct path to the PFX file on the command-line.") - exit(1) - print(f"Creating MSIX package with signing using PFX file at {pfx_path}") -else: - pfx_path = None - print("Creating MSIX package without signing.") - -pri_command = ["makepri", - "new", - "/pr", f"{content_folder.absolute()}", - "/cf", f"{pri_config_file.absolute()}", - "/of", f"{pri_resources_file.absolute()}" - ] - -msix_command = ["makeappx", - "pack", - "/h", "SHA256", - "/d", f"{content_folder.absolute()}", - "/p", f"{blender_package_msix}" - ] -if pfx_path: - sign_command = ["signtool", - "sign", - "/fd", "sha256", - "/a", "/f", f"{pfx_path.absolute()}", - "/p", f"{args.password}", - f"{blender_package_msix}" - ] - -if args.overwrite: - if content_folder.joinpath("Assets").exists(): - shutil.rmtree(content_folder) -content_folder.mkdir(exist_ok=True) -shutil.copytree(assets_original_folder, content_assets_folder) - -manifest_text = pathlib.Path("AppxManifest.xml.template").read_text() -manifest_text = manifest_text.replace("[VERSION]", args.version) -manifest_text = manifest_text.replace("[PUBLISHER]", args.publisher) -manifest_text = manifest_text.replace("[LTSORNOT]", LTSORNOT) -manifest_text = manifest_text.replace("[PACKAGETYPE]", PACKAGETYPE) -pathlib.Path(content_folder, "AppxManifest.xml").write_text(manifest_text) - -if not args.skipdl: - print(f"Downloading blender archive {args.url} to {local_blender_zip}...") - - with open(local_blender_zip, "wb") as download_zip: - response = requests.get(args.url) - download_zip.write(response.content) - - print("... download complete.") -else: - print("Skipping download") - -print(f"Extracting files from ZIP to {content_blender_folder}...") - -# Extract the files from the ZIP archive, but skip the leading part of paths -# in the ZIP. We want to write the files to the content_blender_folder where -# blender.exe ends up as ./Content/Blender/blender.exe, and not -# ./Content/Blender/blender-2.83.3-windows64/blender.exe -with zipfile.ZipFile(local_blender_zip, "r") as blender_zip: - for entry in blender_zip.infolist(): - if entry.is_dir(): - continue - entry_location = pathlib.Path(entry.filename) - target_location = content_blender_folder.joinpath(*entry_location.parts[1:]) - pathlib.Path(target_location.parent).mkdir(parents=True, exist_ok=True) - extracted_entry = blender_zip.read(entry) - target_location.write_bytes(extracted_entry) - -print("... extraction complete.") - - -print(f"Generating Package Resource Index (PRI) file using command: {' '.join(pri_command)}") -execute_command(pri_command, "MakePri", 4) - -print(f"Creating MSIX package using command: {' '.join(msix_command)}") - -# Remove MSIX file if it already exists. Otherwise the MakeAppX tool -# will hang. -if blender_package_msix.exists(): - os.remove(blender_package_msix) -execute_command(msix_command, "MakeAppX", 2) - -if args.pfx: - print(f"Signing MSIX package using command: {' '.join(sign_command)}") - execute_command(sign_command, "SignTool", 3) - -if not args.leavezip: - os.remove(local_blender_zip) -shutil.rmtree(content_folder) - -print("Done.") -- cgit v1.2.3 From 97ccd592ce6cda32171054e28d36ea62a95afe64 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 28 May 2021 15:33:04 +0200 Subject: Fix crash in liboverride resync. Reported by studio (@andy), thanks. --- source/blender/blenkernel/intern/lib_override.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 0e2317c72de..83bd2b6abb7 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -252,6 +252,13 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id) return false; } + /* A bit weird, but those embedded IDs are handled by their owner ID anyway, so we can just + * assume they are never user-edited, actual proper detection will happen from their owner check. + */ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + return false; + } + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) { -- cgit v1.2.3 From adafd7257d0ed1bcca9eae63ed1e87a891c44975 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 28 May 2021 13:38:08 +0200 Subject: Fix T88635: VSE: Select Linked gives unpredictable results Caused by {rB66923031e6f2}. Code would process unselected sequences and skip selected, needs to be the other way around. Maniphest Tasks: T88635 Differential Revision: https://developer.blender.org/D11424 --- source/blender/editors/space_sequencer/sequencer_select.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index a9f8a70d61e..a2bb9bb9ac8 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -808,7 +808,7 @@ static bool select_linked_internal(Scene *scene) bool changed = false; LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if ((seq->flag & SELECT) != 0) { + if ((seq->flag & SELECT) == 0) { continue; } /* Only get unselected neighbors. */ -- cgit v1.2.3 From 418888f1c9a3c3ca8facc04d271d48ad37e5f5b2 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Fri, 28 May 2021 07:57:21 -0600 Subject: MSVC: Fix build error with 16.10/11 Not entirely sure why this was not an issue for 16.9 but TBB includes the Windows.h header which by default will define min and max macro's These collide with the stl versions in This patch requests Windows.h not to define the problematic macro's, resolving the conflict. --- source/blender/modifiers/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 91327b97fe4..0138dd0c3ad 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -195,7 +195,11 @@ endif() 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} ) -- cgit v1.2.3 From 080623b8ac4d21988718a7c9e107d4d5fa29bdce Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 28 May 2021 14:02:24 +0200 Subject: Fix incorrect Denoise node SSE 4.1 warning on macOS Intel --- source/blender/editors/space_node/drawnode.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 5110c14ef4d..510fcd5e887 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -2751,11 +2751,10 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po #else /* Always supported through Accelerate framework BNNS on macOS. */ # ifndef __APPLE__ - if (!BLI_cpu_support_sse41()) -# endif - { + if (!BLI_cpu_support_sse41()) { uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR); } +# endif #endif uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, NULL, ICON_NONE); -- cgit v1.2.3 From 11e32332ddfdcacb7f992d9fb25025b53c5037c1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 28 May 2021 10:42:22 -0400 Subject: Geometry Nodes: Add Mesh to Curve Node This node creates poly curve splines from mesh edges. A selection attribute input allows only using some of the edges from the mesh. The node builds cyclic splines from branchless groups of edges where possible, but when there is a three-way intersection, the spline stops. The node also transfers all attributes from the mesh to the resulting control points. In the future we could add a way to limit that to a subset of the attributes to improve performance. The algorithm is from Animation Nodes, written by @OmarSquircleArt. I added the ability to use a selection, attribute transferring, and used different variable names, etc, but other than that the algorithm is the same. Differential Revision: https://developer.blender.org/D11265 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 318 +++++++++++++++++++++ 7 files changed, 324 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 4bff18cd1be..05c7ef756c7 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -503,6 +503,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_CURVE", "Curve", items=[ NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), + NodeItem("GeometryNodeMeshToCurve"), ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ NodeItem("GeometryNodeBoundBox"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 448f4ae48ad..b3247a751bf 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1426,6 +1426,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 #define GEO_NODE_MATERIAL_REPLACE 1051 +#define GEO_NODE_MESH_TO_CURVE 1052 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3377f5c69dc..d0864e85373 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5066,6 +5066,7 @@ static void registerGeometryNodes() register_node_type_geo_mesh_primitive_ico_sphere(); register_node_type_geo_mesh_primitive_line(); register_node_type_geo_mesh_primitive_uv_sphere(); + register_node_type_geo_mesh_to_curve(); register_node_type_geo_object_info(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9d21ff19f46..24085b31fc3 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -178,6 +178,7 @@ set(SRC geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc geometry/nodes/node_geo_mesh_primitive_line.cc geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc + geometry/nodes/node_geo_mesh_to_curve.cc geometry/nodes/node_geo_object_info.cc geometry/nodes/node_geo_point_distribute.cc geometry/nodes/node_geo_point_instance.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d2a702c30a6..eadfed26be1 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -66,6 +66,7 @@ void register_node_type_geo_mesh_primitive_grid(void); void register_node_type_geo_mesh_primitive_ico_sphere(void); void register_node_type_geo_mesh_primitive_line(void); void register_node_type_geo_mesh_primitive_uv_sphere(void); +void register_node_type_geo_mesh_to_curve(void); void register_node_type_geo_object_info(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index ce1813fdac3..ef5f25e7b57 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -305,6 +305,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") +DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "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", "") 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 new file mode 100644 index 00000000000..b852f929b5f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -0,0 +1,318 @@ +/* + * 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_array.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +using blender::Array; + +static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = { + {SOCK_GEOMETRY, N_("Mesh")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_to_curve_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +namespace blender::nodes { + +template +static void copy_attribute_to_points(const VArray &source_data, + Span map, + MutableSpan dest_data) +{ + for (const int point_index : map.index_range()) { + const int vert_index = map[point_index]; + dest_data[point_index] = source_data[vert_index]; + } +} + +static void copy_attributes_to_points(CurveEval &curve, + const MeshComponent &mesh_component, + Span> point_to_vert_maps) +{ + MutableSpan splines = curve.splines(); + Set source_attribute_names = mesh_component.attribute_names(); + + /* Copy builtin control point attributes. */ + if (source_attribute_names.contains_as("tilt")) { + const GVArray_Typed tilt_attribute = mesh_component.attribute_get_for_read( + "tilt", ATTR_DOMAIN_POINT, 0.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points( + *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); + } + }); + source_attribute_names.remove_contained_as("tilt"); + } + if (source_attribute_names.contains_as("radius")) { + const GVArray_Typed radius_attribute = mesh_component.attribute_get_for_read( + "radius", ATTR_DOMAIN_POINT, 1.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points( + *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); + } + }); + source_attribute_names.remove_contained_as("radius"); + } + + /* Don't copy other builtin control point attributes. */ + source_attribute_names.remove_as("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, + 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. */ + if (!mesh_attribute) { + continue; + } + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); + + 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); + BLI_assert(spline_attribute); + + /* Copy attribute based on the map for this spline. */ + attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_attribute_to_points( + mesh_attribute->typed(), point_to_vert_maps[i], spline_attribute->typed()); + }); + } + }); + } + + curve.assert_valid_point_attributes(); +} + +struct CurveFromEdgesOutput { + std::unique_ptr curve; + Vector> point_to_vert_maps; +}; + +static CurveFromEdgesOutput mesh_to_curve(Span verts, Span> edges) +{ + std::unique_ptr curve = std::make_unique(); + Vector> point_to_vert_maps; + + /* Compute the number of edges connecting to each vertex. */ + Array neighbor_count(verts.size(), 0); + for (const std::pair &edge : edges) { + neighbor_count[edge.first]++; + neighbor_count[edge.second]++; + } + + /* Compute an offset into the array of neighbor edges based on the counts. */ + Array neighbor_offsets(verts.size()); + int start = 0; + for (const int i : verts.index_range()) { + neighbor_offsets[i] = start; + start += neighbor_count[i]; + } + + /* Use as an index into the "neighbor group" for each vertex. */ + Array used_slots(verts.size(), 0); + /* Calculate the indices of each vertex's neighboring edges. */ + Array neighbors(edges.size() * 2); + for (const int i : edges.index_range()) { + const int v1 = edges[i].first; + const int v2 = edges[i].second; + neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; + neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; + used_slots[v1]++; + used_slots[v2]++; + } + + /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ + Array unused_edges = std::move(used_slots); + + for (const int start_vert : verts.index_range()) { + /* The vertex will be part of a cyclic spline. */ + if (neighbor_count[start_vert] == 2) { + continue; + } + + /* The vertex has no connected edges, or they were already used. */ + if (unused_edges[start_vert] == 0) { + continue; + } + + for (const int i : IndexRange(neighbor_count[start_vert])) { + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert] + i]; + + if (unused_edges[next_vert] == 0) { + continue; + } + + std::unique_ptr spline = std::make_unique(); + Vector point_to_vert_map; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we read a vertex with more than two connected edges. */ + while (true) { + int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + if (neighbor_count[current_vert] != 2) { + break; + } + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + } + + /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ + for (const int start_vert : verts.index_range()) { + if (unused_edges[start_vert] != 2) { + continue; + } + + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert]]; + + std::unique_ptr spline = std::make_unique(); + Vector point_to_vert_map; + spline->set_cyclic(true); + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we loop back to the start vertex. */ + while (next_vert != start_vert) { + const int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + + curve->attributes.reallocate(curve->splines().size()); + return {std::move(curve), std::move(point_to_vert_maps)}; +} + +/** + * Get a separate array of the indices for edges in a selection (a boolean attribute). + * This helps to make the above algorithm simpler by removing the need to check for selection + * in many places. + */ +static Vector> get_selected_edges(GeoNodeExecParams params, + const MeshComponent &component) +{ + const Mesh &mesh = *component.get_for_read(); + const std::string selection_name = params.extract_input("Selection"); + if (!selection_name.empty() && !component.attribute_exists(selection_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + selection_name + "\""); + } + GVArray_Typed selection = component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_EDGE, true); + + Vector> selected_edges; + for (const int i : IndexRange(mesh.totedge)) { + if (selection[i]) { + selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); + } + } + + return selected_edges; +} + +static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Mesh"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_mesh()) { + params.set_output("Curve", GeometrySet()); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read(); + const Mesh &mesh = *component.get_for_read(); + Span verts = Span{mesh.mvert, mesh.totvert}; + Span edges = Span{mesh.medge, mesh.totedge}; + if (edges.size() == 0) { + params.set_output("Curve", GeometrySet()); + return; + } + + Vector> selected_edges = get_selected_edges(params, component); + + CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges); + copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps); + + params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release())); +} + +} // namespace blender::nodes + +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); + node_type_socket_templates(&ntype, geo_node_mesh_to_curve_in, geo_node_mesh_to_curve_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From c65f4b3d76deb47017a2e661290481e5e613b5db Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 28 May 2021 11:51:05 -0300 Subject: DrawManager: Early return for buffer cache creation No real functional changes. This is useful for benchmark cases when `cache->uv_cage` is passed but has no buffers are requested. --- source/blender/draw/intern/draw_cache_extract_mesh.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index f167ea3d540..fe162e6ea6d 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -5956,12 +5956,14 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, eMRDataType data_flag = 0; const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose != NULL; + bool do_extract = false; #define TEST_ASSIGN(type, type_lowercase, name) \ do { \ if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \ iter_flag |= mesh_extract_iter_type(&extract_##name); \ data_flag |= extract_##name.data_flag; \ + do_extract = true; \ } \ } while (0) @@ -6000,6 +6002,10 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, TEST_ASSIGN(IBO, ibo, edituv_points); TEST_ASSIGN(IBO, ibo, edituv_fdots); + if (!do_extract) { + return; + } + if (do_lines_loose_subbuffer) { iter_flag |= MR_ITER_LEDGE; } -- cgit v1.2.3 From a4700549e065e86c3d42c93e73f76106f4054512 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 28 May 2021 17:14:01 +0200 Subject: GPencil: Fix unreported random rotation for single point with texture When using ``Path`` alignment, if the stroke has one point the texture rotates randomly when move the viewport. This was because with one point is impossible to calculate a path. Now, if the stroke has only one point, the texture for this stroke is aligned to Object. --- source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index 7412959a30b..ac48b94fea9 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -442,6 +442,10 @@ void stroke_vertex() if (is_dot) { # ifdef GP_MATERIAL_BUFFER_LEN int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT; + /* For one point strokes use object aligment. */ + if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) { + alignement = GP_STROKE_ALIGNMENT_OBJECT; + } # endif vec2 x_axis; -- cgit v1.2.3 From fc2b56e68ccbd7da95d78a059a82035c0965b924 Mon Sep 17 00:00:00 2001 From: James Monteath Date: Fri, 28 May 2021 17:53:02 +0200 Subject: Add branch based pipeline config file used by buildbot and make_update.py script. --- build_files/config/pipeline_config.json | 87 +++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 build_files/config/pipeline_config.json diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json new file mode 100644 index 00000000000..47318b2f7dc --- /dev/null +++ b/build_files/config/pipeline_config.json @@ -0,0 +1,87 @@ +{ + "update-code": + { + "git" : + { + "submodules": + [ + { "path": "release/scripts/addons", "branch": "master", "commit_id": "HEAD" }, + { "path": "release/scripts/addons_contrib", "branch": "master", "commit_id": "HEAD" }, + { "path": "release/datafiles/locale", "branch": "master", "commit_id": "HEAD" }, + { "path": "source/tools", "branch": "master", "commit_id": "HEAD" } + ] + }, + "svn": + { + "tests": { "path": "lib/tests", "branch": "trunk", "commit_id": "HEAD" }, + "libraries": + { + "darwin-x86_64": { "path": "lib/darwin", "branch": "trunk", "commit_id": "HEAD" }, + "darwin-arm64": { "path": "lib/darwin_arm64", "branch": "trunk", "commit_id": "HEAD" }, + "linux-x86_64": { "path": "lib/linux_centos7_x86_64", "branch": "trunk", "commit_id": "HEAD" }, + "windows-amd64": { "path": "lib/win64_vc15", "branch": "trunk", "commit_id": "HEAD" } + } + } + }, + "buildbot": + { + "gcc": + { + "version": "9.0" + }, + "sdks": + { + "optix": + { + "version": "7.1.0" + }, + "cuda10": + { + "version": "10.1" + }, + "cuda11": + { + "version": "11.3" + } + }, + "cmake": + { + "default": + { + "version": "any", + "overrides": + { + + } + }, + "darwin-x86_64": + { + "overrides": + { + + } + }, + "darwin-arm64": + { + "overrides": + { + + } + }, + "linux-x86_64": + { + "overrides": + { + + } + }, + "windows-amd64": + { + "overrides": + { + + } + } + } + } +} -- cgit v1.2.3 From 20e250dae307f9cb833dadc52059193dc1e1b5e5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 28 May 2021 12:04:52 -0400 Subject: Fix T88601: Attribute Compare boolean doesn't expose socket While we could make this node work for boolean inputs in the future, currently it's really just designed to compare "float-like" inputs. Many comparison modes don't even make sense for boolean inputs. Therefore, the simplest fix for this bug is just to disable the boolean attribute input modes for this node. Differential Revision: https://developer.blender.org/D11427 --- source/blender/makesrna/intern/rna_nodetree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b1b2e9738c1..9ef94a0df2f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9148,12 +9148,12 @@ static void def_geo_attribute_attribute_compare(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); RNA_def_property_ui_text(prop, "Input Type A", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); RNA_def_property_ui_text(prop, "Input Type B", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -- cgit v1.2.3 From c36938297753ac45316f0b10b7d3e2f5307a6aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 28 May 2021 18:02:45 +0200 Subject: EEVEE: Fix NaN caused by ensure_valid_reflection() This was caused by unsafe sqrt calls. Fixes T86578 white artifacts in EEVEE Reviewed By: brecht, dfelinto Differential Revision: https://developer.blender.org/D11428 --- source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl | 6 +++--- source/blender/draw/intern/shaders/common_math_lib.glsl | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index c8eaa06094e..05496ad4ab0 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -164,8 +164,8 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) vec2 N_new; if (valid1 && valid2) { /* If both are possible, do the expensive reflection-based check. */ - vec2 N1 = vec2(sqrt(1.0 - N1_z2), sqrt(N1_z2)); - vec2 N2 = vec2(sqrt(1.0 - N2_z2), sqrt(N2_z2)); + vec2 N1 = vec2(safe_sqrt(1.0 - N1_z2), safe_sqrt(N1_z2)); + vec2 N2 = vec2(safe_sqrt(1.0 - N2_z2), safe_sqrt(N2_z2)); float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; @@ -181,7 +181,7 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) } else if (valid1 || valid2) { float Nz2 = valid1 ? N1_z2 : N2_z2; - N_new = vec2(sqrt(1.0 - Nz2), sqrt(Nz2)); + N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2)); } else { return Ng; diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index 33deae0b0a1..479f9cd1827 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -86,6 +86,8 @@ float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; } vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); } vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); } +float safe_sqrt(float a) { return sqrt(max(a, 0.0)); } + float sqr(float a) { return a * a; } vec2 sqr(vec2 a) { return a * a; } vec3 sqr(vec3 a) { return a * a; } -- cgit v1.2.3 From 653bbaa246e3fa57b0b7846704fe5ce432659bb9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 28 May 2021 12:17:04 -0400 Subject: Geometry Nodes: Polish switch node UI Based on the task T88006, there are a few simple changes to make to improve the switch node: - Change the label to "False" / "True" for clarity - Change default to geometry, as it's the basic data container in geometry nodes. - Change node class to `NODE_CLASS_CONVERTOR`, which was an oversight in the original patch. I will add the new socket types (material and texture) in a separate commit. Thanks to @EitanSomething for the original patch. Differential Revision: https://developer.blender.org/D11165 --- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenloader/intern/versioning_300.c | 53 ++++++++++++++++++---- .../nodes/geometry/nodes/node_geo_switch.cc | 44 +++++++++--------- 3 files changed, 66 insertions(+), 33 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 0bab980cfcd..eb937ac9608 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 1 +#define BLENDER_FILE_SUBVERSION 2 /* 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/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 8c5e86eadd3..c175714c537 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -22,6 +22,7 @@ #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_brush_types.h" @@ -85,6 +86,32 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } +static void version_switch_node_input_prefix(Main *bmain) +{ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_SWITCH) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + /* Skip the "switch" socket. */ + if (socket == node->inputs.first) { + continue; + } + strcpy(socket->name, socket->name[0] == 'A' ? "False" : "True"); + + /* Replace "A" and "B", but keep the unique number suffix at the end. */ + char number_suffix[8]; + BLI_strncpy(number_suffix, socket->identifier + 1, sizeof(number_suffix)); + strcpy(socket->identifier, socket->name); + strcat(socket->identifier, number_suffix); + } + } + } + } + } + FOREACH_NODETREE_END; +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -110,6 +137,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 2)) { + version_switch_node_input_prefix(bmain); + + if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->pose == NULL) { + continue; + } + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale); + } + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -121,15 +164,5 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ - if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) { - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->pose == NULL) { - continue; - } - LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale); - } - } - } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 049ba5d3143..742fafba9e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -22,24 +22,24 @@ static bNodeSocketTemplate geo_node_switch_in[] = { {SOCK_BOOLEAN, N_("Switch")}, - {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_BOOLEAN, N_("A")}, - {SOCK_BOOLEAN, N_("B")}, - {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_STRING, N_("A")}, - {SOCK_STRING, N_("B")}, - {SOCK_GEOMETRY, N_("A")}, - {SOCK_GEOMETRY, N_("B")}, - {SOCK_OBJECT, N_("A")}, - {SOCK_OBJECT, N_("B")}, - {SOCK_COLLECTION, N_("A")}, - {SOCK_COLLECTION, N_("B")}, + {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_BOOLEAN, N_("False")}, + {SOCK_BOOLEAN, N_("True")}, + {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_STRING, N_("False")}, + {SOCK_STRING, N_("True")}, + {SOCK_GEOMETRY, N_("False")}, + {SOCK_GEOMETRY, N_("True")}, + {SOCK_OBJECT, N_("False")}, + {SOCK_OBJECT, N_("True")}, + {SOCK_COLLECTION, N_("False")}, + {SOCK_COLLECTION, N_("True")}, {-1, ""}, }; @@ -64,7 +64,7 @@ static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), Pointe static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) { NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__); - data->input_type = SOCK_FLOAT; + data->input_type = SOCK_GEOMETRY; node->storage = data; } @@ -91,8 +91,8 @@ static void output_input(GeoNodeExecParams ¶ms, const StringRef input_suffix, const StringRef output_identifier) { - const std::string name_a = "A" + input_suffix; - const std::string name_b = "B" + input_suffix; + const std::string name_a = "False" + input_suffix; + const std::string name_b = "True" + input_suffix; if (input) { params.set_input_unused(name_a); if (params.lazy_require_input(name_b)) { @@ -165,7 +165,7 @@ void register_node_type_geo_switch() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out); node_type_init(&ntype, geo_node_switch_init); node_type_update(&ntype, blender::nodes::geo_node_switch_update); -- cgit v1.2.3 From 33113506701fac51b8b4dc8f51975200f885e50a Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Fri, 28 May 2021 18:35:26 +0200 Subject: Fix T87932: Failure to build movie strip proxy We didn't initialize the scaled proxy frame properly. This would lead to issues in ffmpeg 4.4 as they are more strict that the API is properly used. Now we initialize the size and format of the frame. --- source/blender/imbuf/intern/indexer.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 11ce77e3091..26f332bd575 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -466,13 +466,6 @@ struct proxy_output_ctx { struct anim *anim; }; -// work around stupid swscaler 16 bytes alignment bug... - -static int round_up(int x, int mod) -{ - return x + ((mod - (x % mod)) % mod); -} - static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality) { @@ -595,15 +588,19 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( if (st->codecpar->width != width || st->codecpar->height != height || st->codecpar->format != rv->c->pix_fmt) { rv->frame = av_frame_alloc(); - av_image_fill_arrays( - rv->frame->data, - rv->frame->linesize, - MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, round_up(width, 16), height, 1), - "alloc proxy output frame"), - rv->c->pix_fmt, - round_up(width, 16), - height, - 1); + + av_image_fill_arrays(rv->frame->data, + rv->frame->linesize, + MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, width, height, 1), + "alloc proxy output frame"), + rv->c->pix_fmt, + width, + height, + 1); + + rv->frame->format = rv->c->pix_fmt; + rv->frame->width = width; + rv->frame->height = height; rv->sws_ctx = sws_getContext(st->codecpar->width, rv->orig_height, -- cgit v1.2.3 From 9225fe933ae9904ea7bb646110308a921b37fc86 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Fri, 28 May 2021 18:37:36 +0200 Subject: Make encoded video fps correct with ffmpeg < 4.4 Before the FFmpeg commit: github.com/FFmpeg/FFmpeg/commit/1c0885334dda9ee8652e60c586fa2e3674056586 FFmpeg would use deprecated variables to calculate the video fps. We don't use these deprecated variables anymore, so ensure that the duration is correct in ffmpeg versions without this fix. Reviewed By: Sergey, Richard Antalik Differential Revision: http://developer.blender.org/D11417 --- intern/ffmpeg/ffmpeg_compat.h | 49 ++++++++++++++++++++++++++ source/blender/blenkernel/intern/writeffmpeg.c | 16 ++++++--- source/blender/imbuf/intern/indexer.c | 3 ++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h index 54a004d53e2..0c22cf82688 100644 --- a/intern/ffmpeg/ffmpeg_compat.h +++ b/intern/ffmpeg/ffmpeg_compat.h @@ -43,6 +43,55 @@ # define FFMPEG_INLINE static inline #endif +#if (LIBAVFORMAT_VERSION_MAJOR < 58) || \ + ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR < 76)) +# define FFMPEG_USE_DURATION_WORKAROUND 1 + +/* Before ffmpeg 4.4, package duration calculation used depricated variables to calculate the + * packet duration. Use the function from commit + * github.com/FFmpeg/FFmpeg/commit/1c0885334dda9ee8652e60c586fa2e3674056586 + * to calculate the correct framerate for ffmpeg < 4.4. + */ + +FFMPEG_INLINE +void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + if (pkt->duration < 0 && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { + av_log(s, + AV_LOG_WARNING, + "Packet with invalid duration %" PRId64 " in stream %d\n", + pkt->duration, + pkt->stream_index); + pkt->duration = 0; + } + + if (pkt->duration) { + return; + } + + switch (st->codecpar->codec_type) { + case AVMEDIA_TYPE_VIDEO: + if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) { + pkt->duration = av_rescale_q(1, av_inv_q(st->avg_frame_rate), st->time_base); + } + else if (st->time_base.num * 1000LL > st->time_base.den) { + pkt->duration = 1; + } + break; + case AVMEDIA_TYPE_AUDIO: { + int frame_size = av_get_audio_frame_duration2(st->codecpar, pkt->size); + if (frame_size && st->codecpar->sample_rate) { + pkt->duration = av_rescale_q( + frame_size, (AVRational){1, st->codecpar->sample_rate}, st->time_base); + } + break; + } + default: + break; + } +} +#endif + FFMPEG_INLINE void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp) { diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 39f65d76e3c..3408f990fd1 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -205,12 +205,11 @@ static int write_audio_frame(FFMpegContext *context) success = -1; } - av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); - if (pkt->duration > 0) { - pkt->duration = av_rescale_q(pkt->duration, c->time_base, context->audio_stream->time_base); - } - pkt->stream_index = context->audio_stream->index; + av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->audio_stream, pkt); +# endif pkt->flags |= AV_PKT_FLAG_KEY; @@ -349,6 +348,10 @@ static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, R packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif + if (av_interleaved_write_frame(context->outfile, packet) != 0) { success = -1; break; @@ -1181,6 +1184,9 @@ static void flush_ffmpeg(FFMpegContext *context) packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif int write_ret = av_interleaved_write_frame(context->outfile, packet); if (write_ret != 0) { diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 26f332bd575..453df6078ce 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -683,6 +683,9 @@ static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fr packet->stream_index = ctx->st->index; av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(ctx->of, ctx->st, packet); +# endif int write_ret = av_interleaved_write_frame(ctx->of, packet); if (write_ret != 0) { -- cgit v1.2.3 From 74c7e21f6c82095f48b7fe2973f0779394e2dbed Mon Sep 17 00:00:00 2001 From: James Monteath Date: Fri, 28 May 2021 19:46:53 +0200 Subject: Add and update README.md files for CI script removal --- build_files/buildbot/README.md | 73 ++------------------------------ build_files/config/README.md | 9 ++++ build_files/utils/README.md | 5 +++ release/darwin/README.md | 5 +++ release/freedesktop/snap/README.md | 5 +++ release/windows/msix/README.md | 85 ++------------------------------------ 6 files changed, 32 insertions(+), 150 deletions(-) create mode 100644 build_files/config/README.md create mode 100644 build_files/utils/README.md create mode 100644 release/darwin/README.md create mode 100644 release/freedesktop/snap/README.md diff --git a/build_files/buildbot/README.md b/build_files/buildbot/README.md index 06733c9a42d..9c71deeec68 100644 --- a/build_files/buildbot/README.md +++ b/build_files/buildbot/README.md @@ -1,70 +1,5 @@ -Blender Buildbot -================ +Moved Scripts +============= -Code signing ------------- - -Code signing is done as part of INSTALL target, which makes it possible to sign -files which are aimed into a bundle and coming from a non-signed source (such as -libraries SVN). - -This is achieved by specifying `worker_codesign.cmake` as a post-install script -run by CMake. This CMake script simply involves an utility script written in -Python which takes care of an actual signing. - -### Configuration - -Client configuration doesn't need anything special, other than variable -`SHARED_STORAGE_DIR` pointing to a location which is watched by a server. -This is done in `config_builder.py` file and is stored in Git (which makes it -possible to have almost zero-configuration buildbot machines). - -Server configuration requires copying `config_server_template.py` under the -name of `config_server.py` and tweaking values, which are platform-specific. - -#### Windows configuration - -There are two things which are needed on Windows in order to have code signing -to work: - -- `TIMESTAMP_AUTHORITY_URL` which is most likely set http://timestamp.digicert.com -- `CERTIFICATE_FILEPATH` which is a full file path to a PKCS #12 key (.pfx). - -## Tips - -### Self-signed certificate on Windows - -It is easiest to test configuration using self-signed certificate. - -The certificate manipulation utilities are coming with Windows SDK. -Unfortunately, they are not added to PATH. Here is an example of how to make -sure they are easily available: - -``` -set PATH=C:\Program Files (x86)\Windows Kits\10\App Certification Kit;%PATH% -set PATH=C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64;%PATH% -``` - -Generate CA: - -``` -makecert -r -pe -n "CN=Blender Test CA" -ss CA -sr CurrentUser -a sha256 ^ - -cy authority -sky signature -sv BlenderTestCA.pvk BlenderTestCA.cer -``` - -Import the generated CA: - -``` -certutil -user -addstore Root BlenderTestCA.cer -``` - -Create self-signed certificate and pack it into PKCS #12: - -``` -makecert -pe -n "CN=Blender Test SPC" -a sha256 -cy end ^ - -sky signature ^ - -ic BlenderTestCA.cer -iv BlenderTestCA.pvk ^ - -sv BlenderTestSPC.pvk BlenderTestSPC.cer - -pvk2pfx -pvk BlenderTestSPC.pvk -spc BlenderTestSPC.cer -pfx BlenderTestSPC.pfx -``` \ No newline at end of file +Scripts have been moved to own git repo +Only configurations remain here and is used with new pipeline diff --git a/build_files/config/README.md b/build_files/config/README.md new file mode 100644 index 00000000000..a939399769c --- /dev/null +++ b/build_files/config/README.md @@ -0,0 +1,9 @@ +Pipeline Config +=============== + +Scripts have been moved to own git repo +This branched based confiration is used by the new build pipeline for the `update-code` step + +It will soon be user ../utils/make_update.py script + +Both buildbot and developers will use the same conconfigurations. diff --git a/build_files/utils/README.md b/build_files/utils/README.md new file mode 100644 index 00000000000..e78d05b0c69 --- /dev/null +++ b/build_files/utils/README.md @@ -0,0 +1,5 @@ +Make Utility Scripts +==================== + +Scripts used only by developers for now + diff --git a/release/darwin/README.md b/release/darwin/README.md new file mode 100644 index 00000000000..9c71deeec68 --- /dev/null +++ b/release/darwin/README.md @@ -0,0 +1,5 @@ +Moved Scripts +============= + +Scripts have been moved to own git repo +Only configurations remain here and is used with new pipeline diff --git a/release/freedesktop/snap/README.md b/release/freedesktop/snap/README.md new file mode 100644 index 00000000000..9c71deeec68 --- /dev/null +++ b/release/freedesktop/snap/README.md @@ -0,0 +1,5 @@ +Moved Scripts +============= + +Scripts have been moved to own git repo +Only configurations remain here and is used with new pipeline diff --git a/release/windows/msix/README.md b/release/windows/msix/README.md index 3f661a44066..9c71deeec68 100644 --- a/release/windows/msix/README.md +++ b/release/windows/msix/README.md @@ -1,82 +1,5 @@ -create_msix_package -=================== +Moved Scripts +============= -This tool is used to create MSIX packages from a given ZiP archive. The MSIX -package is distributed mainly through the Microsoft Store. It can also be -installed when downloaded from blender.org. For that to work the MSIX package -needs to be signed. - -Requirements -============ - -* MakeAppX.exe - this tool is distributed with the Windows 10 SDK and is used to build the .appx package. -* MakePri.exe - this tool is distributed with the Windows 10 SDK and is used to generate a resources file. -* SignTool.exe - this tool is distributed with the Windows 10 SDK and is used to sign the .appx package. -* Python 3 (3.7 or later tested) - to run the create_msix_package.py script -* requests module - can be installed with `pip install requests` -* PFX file (optional, but strongly recommended) - for signing the resulting MSIX - package. **NOTE:** If the MSIX package is not signed when uploaded to the Microsoft - store the validation and certification process can take up to three full - business day. - -Usage -===== - -On the command-line: -```batch -set VERSION=2.83.4.0 -set URL=https://download.blender.org/release/Blender2.83/blender-2.83.4-windows64.zip -set PUBID=CN=PUBIDHERE -set PFX=X:\path\to\cert.pfx -set PFXPW=pwhere - -python create_msix_package.py --version %VERSION% --url %URL% --publisher %PUBID% --pfx %PFX% --password %PFXPW% -``` - -Result will be a MSIX package with the name `blender-2.83.4-windows64.msix`. -With the above usage it will be signed. If the signing options are left out the -package will not be signed. - -Optional arguments -================== - -In support of testing and developing the manifest and scripts there are a few -optional arguments: - -* `--skipdl` : If a `blender.zip` is available already next to the tool use this - to skip actual downloading of the archive designated by `--url`. The latter - option is still required -* `--overwrite` : When script fails the final clean-up may be incomplete leaving - the `Content` folder with its structure. Specify this argument to automatically - clean up this folder before starting to seed the `Content` folder -* `--leavezip` : When specified leave the `blender.zip` file while cleaning up - all other intermediate files, including the `Content` folder. This is useful - to not have to re-download the same archive from `--url` on each usage - - -What it does -============ - -The tool creates in the directory it lives a subfolder called `Content`. This is -where all necessary files are placed. - -The `Assets` folder is copied to the `Content` folder. - -From the application manifest template a version with necessary parts replaced as -their actual values as specified on the command-line is realized. This manifest controls the packaging of Blender into the MSIX format. - -Next the tool downloads the designated ZIP archive locally as blender.zip. From -this archive the files are extracted into the `Content\Blender` folder, but skip -the leading part of paths in the ZIP. We want to write the files to the -content_blender_folder where blender.exe ends up as -`Content\Blender\blender.exe`, and not -`Content\Blender\blender-2.83.4-windows64\blender.exe` - -Once the extraction is completed the MakeAppX tool is executed with the `Content` -folder as input. The result will be the MSIX package with the name in the form -`blender-X.YY.Z-windows64.msix`. - -If the PFX file and its password are given on the command-line this MSIX package -will be signed. - -All intermediate files and directories will be removed. +Scripts have been moved to own git repo +Only configurations remain here and is used with new pipeline -- cgit v1.2.3 From ffe7a41540af2c6909312012d0caba4c22205d9a Mon Sep 17 00:00:00 2001 From: James Monteath Date: Fri, 28 May 2021 20:50:52 +0200 Subject: Fix typos --- build_files/config/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build_files/config/README.md b/build_files/config/README.md index a939399769c..958a01d9aa2 100644 --- a/build_files/config/README.md +++ b/build_files/config/README.md @@ -1,9 +1,10 @@ Pipeline Config =============== -Scripts have been moved to own git repo -This branched based confiration is used by the new build pipeline for the `update-code` step +Scripts have been moved to own git repo. -It will soon be user ../utils/make_update.py script +This configuration file is used by buildbot new pipeline for the `update-code` step. -Both buildbot and developers will use the same conconfigurations. +It will soon be used by the ../utils/make_update.py script. + +Both buildbot and developers will eventually use the same configuration file. \ No newline at end of file -- cgit v1.2.3 From 3c02e648f395f8b37eecc70e7abc2ce05528c09f Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 28 May 2021 17:14:01 +0200 Subject: GPencil: Fix unreported random rotation for single point with texture When using ``Path`` alignment, if the stroke has one point the texture rotates randomly when move the viewport. This was because with one point is impossible to calculate a path. Now, if the stroke has only one point, the texture for this stroke is aligned to Object. --- source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index 7412959a30b..ac48b94fea9 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -442,6 +442,10 @@ void stroke_vertex() if (is_dot) { # ifdef GP_MATERIAL_BUFFER_LEN int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT; + /* For one point strokes use object aligment. */ + if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) { + alignement = GP_STROKE_ALIGNMENT_OBJECT; + } # endif vec2 x_axis; -- cgit v1.2.3 From 30f7acfffa427f6de42aefbf829a408cb3411b19 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Fri, 28 May 2021 15:32:12 -0400 Subject: Docs: Add relevant OCIO envvars to Blender's help message --- source/creator/CMakeLists.txt | 4 ++++ source/creator/creator_args.c | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 6a768106d9e..92cc4ae297a 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -119,6 +119,10 @@ if(WITH_GMP) add_definitions(-DWITH_GMP) endif() +if(WITH_OPENCOLORIO) + add_definitions(-DWITH_OCIO) +endif() + # Setup the exe sources and buildinfo set(SRC creator.c diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 36fdaef507b..f9492f5bb62 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -674,6 +674,15 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo printf(" $BLENDER_USER_DATAFILES Directory for user data files (icons, translations, ..).\n"); printf(" $BLENDER_SYSTEM_DATAFILES Directory for system wide data files.\n"); printf(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n"); +# ifdef WITH_OCIO + printf(" $OCIO Path to override the OpenColorIO config file.\n"); + printf( + " $OCIO_ACTIVE_DISPLAYS Overrides the active_displays list from the config file and " + "reorders them. Colon-separated list of displays, e.g 'sRGB:P3'.\n"); + printf( + " $OCIO_ACTIVE_VIEWS Overrides the active_views list from the config file and " + "reorders them. Colon-separated list of view names, e.g 'internal:client:DI'.\n"); +# endif # ifdef WIN32 printf(" $TEMP Store temporary files here.\n"); # else -- cgit v1.2.3 From 6f86d50b9271e23d3d2ca3beba91665078860289 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sat, 29 May 2021 11:21:29 -0400 Subject: UI: Match tooltip with interface name --- release/scripts/startup/bl_operators/uvcalc_lightmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py index 29c17711c2a..6ba8750e9df 100644 --- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -628,7 +628,7 @@ class LightMapPack(Operator): name="New Image", description=( "Assign new images for every mesh (only one if " - "shared tex space enabled)" + "Share Texture Space is enabled)" ), default=False, ) -- cgit v1.2.3 From 870aaf3fb645a2894dd1c0fb34f23b650ec7cab3 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sat, 29 May 2021 11:22:17 -0400 Subject: Docs: Add documentation for 'material_index' Fixes T88485 --- source/blender/makesrna/intern/rna_curve.c | 4 ++-- source/blender/makesrna/intern/rna_gpencil.c | 2 +- source/blender/makesrna/intern/rna_mesh.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index abc96ddc820..e5e7564f578 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -1376,7 +1376,7 @@ static void rna_def_charinfo(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); // RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this character"); RNA_def_property_int_funcs(prop, "rna_ChariInfo_material_index_get", "rna_ChariInfo_material_index_set", @@ -2068,7 +2068,7 @@ static void rna_def_curve_nurb(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this curve"); RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Curve_material_index_range"); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 5f865f9d4cd..19ed5f960cf 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -1625,7 +1625,7 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) /* Material Index */ prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", "Index of material used in this stroke"); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this stroke"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Settings */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index f772f9b5573..d5a1047d287 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1823,7 +1823,7 @@ static void rna_def_mlooptri(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_int_funcs(prop, "rna_MeshLoopTriangle_material_index_get", NULL, NULL); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this triangle"); prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -1933,7 +1933,7 @@ static void rna_def_mpolygon(BlenderRNA *brna) prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "mat_nr"); - RNA_def_property_ui_text(prop, "Material Index", ""); + RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this polygon"); # if 0 RNA_def_property_int_funcs(prop, NULL, NULL, "rna_MeshPoly_material_index_range"); # endif -- cgit v1.2.3 From f5d14e36e8a0974f90780924683b9db4909d8754 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sat, 29 May 2021 12:16:13 -0400 Subject: PyDoc: Use em dash instead of comma for enum items --- doc/python_api/sphinx_doc_gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 032f8a86bd5..64a8ef64b23 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1246,7 +1246,7 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False): "%s.\n" % ( identifier, # Account for multi-line enum descriptions, allowing this to be a block of text. - indent(", ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "), + indent(" -- ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "), ) for identifier, name, description in prop.enum_items ]) -- cgit v1.2.3 From 7b5796dcaa95ebb83973539a2400654165375e1d Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 30 May 2021 11:07:38 -0400 Subject: Docs: clarify description and usage of 'bpy.app.version_file' Fixes T88669 --- source/blender/python/intern/bpy_app.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 927ec11c376..4de6063098b 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -82,7 +82,10 @@ static PyTypeObject BlenderAppType; static PyStructSequence_Field app_info_fields[] = { {"version", "The Blender version as a tuple of 3 numbers. eg. (2, 83, 1)"}, - {"version_file", "The blend file version, compatible with ``bpy.data.version``"}, + {"version_file", + "The Blender version, as a tuple, last used to save a .blend file, compatible with " + "``bpy.data.version``. This value should be used for handling compatibility changes between " + "Blender versions"}, {"version_string", "The Blender version formatted as a string"}, {"version_cycle", "The release status of this build alpha/beta/rc/release"}, {"version_char", "Deprecated, always an empty string"}, -- cgit v1.2.3 From e6a69f76535b3a0c65b88265d614007deb9fe760 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 30 May 2021 11:09:01 -0400 Subject: Docs: Capitalize first word of sentence --- doc/python_api/sphinx_doc_gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 64a8ef64b23..4be27e0f0e8 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -954,7 +954,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra): # constant, not much fun we can do here except to list it. # TODO, figure out some way to document these! fw(".. data:: %s\n\n" % attribute) - write_indented_lines(" ", fw, "constant value %s" % repr(value), False) + write_indented_lines(" ", fw, "Constant value %s" % repr(value), False) fw("\n") else: BPY_LOGGER.debug("\tnot documenting %s.%s of %r type" % (module_name, attribute, value_type.__name__)) -- cgit v1.2.3 From a1556fa05ca96e43b70aa4f6b6284eb581701e4d Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Sun, 30 May 2021 16:37:49 -0400 Subject: Boolean: applying patch D11431 to speed up hole-tolerant raycast. This patch from Erik Abrahamsson uses a parallel_for to speed up the case where the input is not manifold and the "hole_tolerant" option is set. In a test case on a 24 core (48 thread) machine, this sped up a the boolean part on an object with 221k triangles from 12.06s to 0.46s. --- source/blender/blenlib/intern/mesh_boolean.cc | 88 ++++++++++++++++----------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 25291b8c3b0..431720761bc 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -41,6 +41,7 @@ # include "BLI_set.hh" # include "BLI_span.hh" # include "BLI_stack.hh" +# include "BLI_task.hh" # include "BLI_vector.hh" # include "BLI_vector_set.hh" @@ -48,6 +49,10 @@ # include "BLI_mesh_boolean.hh" +# ifdef WITH_TBB +# include "tbb/spin_mutex.h" +# endif + // # define PERFDEBUG namespace blender::meshintersect { @@ -2567,47 +2572,58 @@ static IMesh raycast_tris_boolean(const IMesh &tm, BVHTree *tree = raycast_tree(tm); Vector out_faces; out_faces.reserve(tm.face_size()); - Array in_shape(nshapes, 0); - Array winding(nshapes, 0); - for (int t : tm.face_index_range()) { - Face &tri = *tm.face(t); - int shape = shape_fn(tri.orig); - if (dbg_level > 0) { - std::cout << "process triangle " << t << " = " << &tri << "\n"; - std::cout << "shape = " << shape << "\n"; - } - test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape); - for (int other_shape = 0; other_shape < nshapes; ++other_shape) { - if (other_shape == shape) { - continue; - } - /* The in_shape array has a confidence value for "insideness". - * For most operations, even a hint of being inside - * gives good results, but when shape is a cutter in a Difference - * operation, we want to be pretty sure that the point is inside other_shape. - * E.g., T75827. - * Also, when the operation is intersection, we also want high confidence. - */ - bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) || - op == BoolOpType::Intersect; - bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f); +# ifdef WITH_TBB + tbb::spin_mutex mtx; +# endif + const int grainsize = 256; + parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) { + Array in_shape(nshapes, 0); + Array winding(nshapes, 0); + for (int t : range) { + Face &tri = *tm.face(t); + int shape = shape_fn(tri.orig); if (dbg_level > 0) { - std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape " - << other_shape << " val = " << in_shape[other_shape] << "\n"; + std::cout << "process triangle " << t << " = " << &tri << "\n"; + std::cout << "shape = " << shape << "\n"; } - winding[other_shape] = inside; - } - bool do_flip; - bool do_remove = raycast_test_remove(op, winding, shape, &do_flip); - if (!do_remove) { - if (!do_flip) { - out_faces.append(&tri); + test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape); + for (int other_shape = 0; other_shape < nshapes; ++other_shape) { + if (other_shape == shape) { + continue; + } + /* The in_shape array has a confidence value for "insideness". + * For most operations, even a hint of being inside + * gives good results, but when shape is a cutter in a Difference + * operation, we want to be pretty sure that the point is inside other_shape. + * E.g., T75827. + * Also, when the operation is intersection, we also want high confidence. + */ + bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) || + op == BoolOpType::Intersect; + bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f); + if (dbg_level > 0) { + std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape " + << other_shape << " val = " << in_shape[other_shape] << "\n"; + } + winding[other_shape] = inside; } - else { - raycast_add_flipped(out_faces, tri, arena); + bool do_flip; + bool do_remove = raycast_test_remove(op, winding, shape, &do_flip); + { +# ifdef WITH_TBB + tbb::spin_mutex::scoped_lock lock(mtx); +# endif + if (!do_remove) { + if (!do_flip) { + out_faces.append(&tri); + } + else { + raycast_add_flipped(out_faces, tri, arena); + } + } } } - } + }); BLI_bvhtree_free(tree); ans.set_faces(out_faces); return ans; -- cgit v1.2.3 From 5f749a03ca1d34296adc84fc251e15790f583021 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 09:32:37 +0200 Subject: Fix T88456: DrawManager: Keep subset RenderMeshData around when geometry does not change. Reuse loose geometry during selection (and other operations) from previous calculation. Loose geometry stays the same, but was recalculated to determine the size of GPU buffers. This patch would reuse the previous loose geometry when geometry wasn't changed. Although not the main bottleneck during selection it is measurable. Master. `rdata 46ms iter 55ms (frame 410ms)` This patch. `rdata 5ms iter 52ms (frame 342ms)` Reviewed By: mano-wii Differential Revision: https://developer.blender.org/D11339 --- source/blender/draw/intern/draw_cache_extract.h | 17 +++ .../blender/draw/intern/draw_cache_extract_mesh.c | 140 +++++++++++---------- source/blender/draw/intern/draw_cache_impl_mesh.c | 36 ++++-- 3 files changed, 121 insertions(+), 72 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index a3e43ff2ec5..14cbba82fba 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -150,6 +150,18 @@ typedef struct MeshBufferCache { GPUIndexBuf **tris_per_mat; } MeshBufferCache; +/** + * Data that are kept around between extractions to reduce rebuilding time. + * + * - Loose geometry. + */ +typedef struct MeshBufferExtractionCache { + int edge_loose_len; + int vert_loose_len; + int *lverts; + int *ledges; +} MeshBufferExtractionCache; + typedef enum DRWBatchFlag { MBC_SURFACE = (1 << 0), MBC_SURFACE_WEIGHTS = (1 << 1), @@ -195,6 +207,10 @@ typedef enum DRWBatchFlag { typedef struct MeshBatchCache { MeshBufferCache final, cage, uv_cage; + MeshBufferExtractionCache final_extraction_cache; + MeshBufferExtractionCache cage_extraction_cache; + MeshBufferExtractionCache uv_cage_extraction_cache; + struct { /* Surfaces / Render */ GPUBatch *surface; @@ -271,6 +287,7 @@ typedef struct MeshBatchCache { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache mbc, + MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, const bool is_paint_mode, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index fe162e6ea6d..62d8040e88b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -134,79 +134,85 @@ typedef struct MeshRenderData { int *lverts, *ledges; } MeshRenderData; -static void mesh_render_data_update_loose_geom(MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType UNUSED(data_flag)) +static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) { + mr->ledges = cache->ledges; + mr->lverts = cache->lverts; + mr->vert_loose_len = cache->vert_loose_len; + mr->edge_loose_len = cache->edge_loose_len; + + mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); +} + +static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges + * and verts are calculated at the same time.*/ + if (cache->lverts) { + return; + } + + cache->vert_loose_len = 0; + cache->edge_loose_len = 0; + if (mr->extract_type != MR_EXTRACT_BMESH) { /* Mesh */ - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - mr->vert_loose_len = 0; - mr->edge_loose_len = 0; - BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); + BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); - mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__); - const MEdge *med = mr->medge; - for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { - if (med->flag & ME_LOOSEEDGE) { - mr->ledges[mr->edge_loose_len++] = med_index; - } - /* Tag verts as not loose. */ - BLI_BITMAP_ENABLE(lvert_map, med->v1); - BLI_BITMAP_ENABLE(lvert_map, med->v2); - } - if (mr->edge_loose_len < mr->edge_len) { - mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + const MEdge *med = mr->medge; + for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { + if (med->flag & ME_LOOSEEDGE) { + cache->ledges[cache->edge_loose_len++] = med_index; } + /* Tag verts as not loose. */ + BLI_BITMAP_ENABLE(lvert_map, med->v1); + BLI_BITMAP_ENABLE(lvert_map, med->v2); + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } - mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - for (int v = 0; v < mr->vert_len; v++) { - if (!BLI_BITMAP_TEST(lvert_map, v)) { - mr->lverts[mr->vert_loose_len++] = v; - } + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); + for (int v = 0; v < mr->vert_len; v++) { + if (!BLI_BITMAP_TEST(lvert_map, v)) { + cache->lverts[cache->vert_loose_len++] = v; } - if (mr->vert_loose_len < mr->vert_len) { - mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); - } - - MEM_freeN(lvert_map); - - mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + MEM_freeN(lvert_map); } else { /* #BMesh */ BMesh *bm = mr->bm; - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - int elem_id; - BMIter iter; - BMVert *eve; - BMEdge *ede; - mr->vert_loose_len = 0; - mr->edge_loose_len = 0; - - mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { - if (eve->e == NULL) { - mr->lverts[mr->vert_loose_len++] = elem_id; - } - } - if (mr->vert_loose_len < mr->vert_len) { - mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts)); - } + int elem_id; + BMIter iter; + BMVert *eve; + BMEdge *ede; - mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__); - BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { - if (ede->l == NULL) { - mr->ledges[mr->edge_loose_len++] = elem_id; - } - } - if (mr->edge_loose_len < mr->edge_len) { - mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges)); + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__); + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { + if (eve->e == NULL) { + cache->lverts[cache->vert_loose_len++] = elem_id; } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } - mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2; + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { + if (ede->l == NULL) { + cache->ledges[cache->edge_loose_len++] = elem_id; + } + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); } } } @@ -317,6 +323,7 @@ static void mesh_render_data_update_normals(MeshRenderData *mr, * otherwise don't use modifiers as they are not from this object. */ static MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, const bool is_editmode, const bool is_paint_mode, const bool is_mode_active, @@ -325,8 +332,7 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, const bool do_uvedit, const DRW_MeshCDMask *UNUSED(cd_used), const ToolSettings *ts, - const eMRIterType iter_type, - const eMRDataType data_flag) + const eMRIterType iter_type) { MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); mr->toolsettings = ts; @@ -435,7 +441,11 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, mr->poly_len = bm->totface; mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); } - mesh_render_data_update_loose_geom(mr, iter_type, data_flag); + + if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { + mesh_render_data_loose_geom_ensure(mr, cache); + mesh_render_data_loose_geom_load(mr, cache); + } return mr; } @@ -446,8 +456,9 @@ static void mesh_render_data_free(MeshRenderData *mr) MEM_SAFE_FREE(mr->poly_normals); MEM_SAFE_FREE(mr->loop_normals); - MEM_SAFE_FREE(mr->lverts); - MEM_SAFE_FREE(mr->ledges); + /* Loose geometry are owned by MeshBufferExtractionCache. */ + mr->ledges = NULL; + mr->lverts = NULL; MEM_freeN(mr); } @@ -5908,6 +5919,7 @@ static void extract_task_create(struct TaskGraph *task_graph, void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, MeshBufferCache mbc, + MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, @@ -6017,6 +6029,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, #endif MeshRenderData *mr = mesh_render_data_create(me, + extraction_cache, is_editmode, is_paint_mode, is_mode_active, @@ -6025,8 +6038,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, do_uvedit, cd_layer_used, ts, - iter_flag, - data_flag); + iter_flag); mr->use_hide = use_hide; mr->use_subsurf_fdots = use_subsurf_fdots; mr->use_final_mesh = do_final; diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 8d5fdcc5276..c2dc9b3ad8d 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -707,6 +707,26 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) } } +static void mesh_buffer_cache_clear(MeshBufferCache *mbufcache) +{ + GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; + GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; + for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) { + GPU_VERTBUF_DISCARD_SAFE(vbos[i]); + } + for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) { + GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); + } +} + +static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extraction_cache) +{ + MEM_SAFE_FREE(extraction_cache->lverts); + MEM_SAFE_FREE(extraction_cache->ledges); + extraction_cache->edge_loose_len = 0; + extraction_cache->vert_loose_len = 0; +} + static void mesh_batch_cache_clear(Mesh *me) { MeshBatchCache *cache = me->runtime.batch_cache; @@ -714,16 +734,13 @@ static void mesh_batch_cache_clear(Mesh *me) return; } FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo; - GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo; - for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) { - GPU_VERTBUF_DISCARD_SAFE(vbos[i]); - } - for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) { - GPU_INDEXBUF_DISCARD_SAFE(ibos[i]); - } + mesh_buffer_cache_clear(mbufcache); } + mesh_buffer_extraction_cache_clear(&cache->final_extraction_cache); + mesh_buffer_extraction_cache_clear(&cache->cage_extraction_cache); + mesh_buffer_extraction_cache_clear(&cache->uv_cage_extraction_cache); + for (int i = 0; i < cache->mat_len; i++) { GPU_INDEXBUF_DISCARD_SAFE(cache->final.tris_per_mat[i]); } @@ -1543,6 +1560,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, cache->uv_cage, + &cache->uv_cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1561,6 +1579,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, cache->cage, + &cache->cage_extraction_cache, me, is_editmode, is_paint_mode, @@ -1578,6 +1597,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, cache->final, + &cache->final_extraction_cache, me, is_editmode, is_paint_mode, -- cgit v1.2.3 From 421c0b45e5015547e98112016bc550da5296e334 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 31 May 2021 10:20:23 +0200 Subject: Fix (studio-reported) crash in collection management code. Code checking for potential collection loop dependencies can be called in cases where we cannot guarantee that there is no NULL pointers, so we need to check those. Was already done for objects. --- source/blender/blenkernel/intern/collection.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index d8fbdf26d93..be827cd338d 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1423,7 +1423,8 @@ static bool collection_instance_find_recursive(Collection *collection, } LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { - if (collection_instance_find_recursive(collection_child->collection, instance_collection)) { + if (collection_child->collection != NULL && + collection_instance_find_recursive(collection_child->collection, instance_collection)) { return true; } } -- cgit v1.2.3 From 26fb7b9474c46c8e1fb64230d66610ebf3b29b7f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 31 May 2021 11:12:39 +0200 Subject: Geometry Nodes: do not create unnecessary geometry components Previously, making instances real would always create an (empty) volume and curve component, even when not necessary. This also fixes T88653. --- .../blenkernel/intern/geometry_set_instances.cc | 26 +++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 9abd00c2b4f..69840ba1612 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -544,9 +544,9 @@ static void join_attributes(Span set_groups, } } -static void join_curve_splines(Span set_groups, CurveComponent &result) +static CurveEval *join_curve_splines(Span set_groups) { - CurveEval *new_curve = new CurveEval(); + Vector new_splines; for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; if (!set.has_curve()) { @@ -558,10 +558,18 @@ static void join_curve_splines(Span set_groups, CurveComp for (const float4x4 &transform : set_group.transforms) { SplinePtr new_spline = source_spline->copy(); new_spline->transform(transform); - new_curve->add_spline(std::move(new_spline)); + new_splines.append(std::move(new_spline)); } } } + if (new_splines.is_empty()) { + return nullptr; + } + + CurveEval *new_curve = new CurveEval(); + for (SplinePtr &new_spline : new_splines) { + new_curve->add_spline(std::move(new_spline)); + } for (SplinePtr &spline : new_curve->splines()) { /* Spline instances should have no custom attributes, since they always come @@ -573,8 +581,7 @@ static void join_curve_splines(Span set_groups, CurveComp } new_curve->attributes.reallocate(new_curve->splines().size()); - - result.replace(new_curve); + return new_curve; } static void join_instance_groups_mesh(Span set_groups, @@ -639,14 +646,17 @@ static void join_instance_groups_volume(Span set_groups, { /* Not yet supported. Joining volume grids with the same name requires resampling of at least * one of the grids. The cell size of the resulting volume has to be determined somehow. */ - VolumeComponent &dst_component = result.get_component_for_write(); - UNUSED_VARS(set_groups, dst_component); + UNUSED_VARS(set_groups, result); } static void join_instance_groups_curve(Span set_groups, GeometrySet &result) { + CurveEval *curve = join_curve_splines(set_groups); + if (curve == nullptr) { + return; + } CurveComponent &dst_component = result.get_component_for_write(); - join_curve_splines(set_groups, dst_component); + dst_component.replace(curve); } GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) -- cgit v1.2.3 From 632bfee0a5ea92d213b5c9d010483c16f0baa636 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 11:14:40 +0200 Subject: Added v2.93 pipeline config for new buildbot. --- build_files/config/pipeline_config.json | 91 +++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 build_files/config/pipeline_config.json diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json new file mode 100644 index 00000000000..1301ff1862d --- /dev/null +++ b/build_files/config/pipeline_config.json @@ -0,0 +1,91 @@ +{ + "update-code": { + "git": { + "submodules": [ + { + "path": "release/scripts/addons", + "branch": "blender-v2.93-release", + "commit_id": "HEAD" + }, + { + "path": "release/scripts/addons_contrib", + "branch": "blender-v2.93-release", + "commit_id": "HEAD" + }, + { + "path": "release/datafiles/locale", + "branch": "blender-v2.93-release", + "commit_id": "HEAD" + }, + { + "path": "source/tools", + "branch": "blender-v2.93-release", + "commit_id": "HEAD" + } + ] + }, + "svn": { + "tests": { + "path": "lib/tests", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + }, + "libraries": { + "darwin-x86_64": { + "path": "lib/darwin", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + }, + "darwin-arm64": { + "path": "lib/darwin_arm64", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + }, + "linux-x86_64": { + "path": "lib/linux_centos7_x86_64", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + }, + "windows-amd64": { + "path": "lib/win64_vc15", + "branch": "tags/blender-2.93-release", + "commit_id": "HEAD" + } + } + } + }, + "buildbot": { + "gcc": { + "version": "9.0" + }, + "sdks": { + "optix": { + "version": "7.1.0" + }, + "cuda10": { + "version": "10.1" + }, + "cuda11": { + "version": "11.3" + } + }, + "cmake": { + "default": { + "version": "any", + "overrides": {} + }, + "darwin-x86_64": { + "overrides": {} + }, + "darwin-arm64": { + "overrides": {} + }, + "linux-x86_64": { + "overrides": {} + }, + "windows-amd64": { + "overrides": {} + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 2534609262aa358d1a921c2f76a1aea6ff780f50 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 11:16:13 +0200 Subject: Revert "Added v2.93 pipeline config for new buildbot." This reverts commit 632bfee0a5ea92d213b5c9d010483c16f0baa636. This config is only intended for 2.93. Master will get its own config file after testing that 2.93 is correct. --- build_files/config/pipeline_config.json | 91 --------------------------------- 1 file changed, 91 deletions(-) delete mode 100644 build_files/config/pipeline_config.json diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json deleted file mode 100644 index 1301ff1862d..00000000000 --- a/build_files/config/pipeline_config.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "update-code": { - "git": { - "submodules": [ - { - "path": "release/scripts/addons", - "branch": "blender-v2.93-release", - "commit_id": "HEAD" - }, - { - "path": "release/scripts/addons_contrib", - "branch": "blender-v2.93-release", - "commit_id": "HEAD" - }, - { - "path": "release/datafiles/locale", - "branch": "blender-v2.93-release", - "commit_id": "HEAD" - }, - { - "path": "source/tools", - "branch": "blender-v2.93-release", - "commit_id": "HEAD" - } - ] - }, - "svn": { - "tests": { - "path": "lib/tests", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - }, - "libraries": { - "darwin-x86_64": { - "path": "lib/darwin", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - }, - "darwin-arm64": { - "path": "lib/darwin_arm64", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - }, - "linux-x86_64": { - "path": "lib/linux_centos7_x86_64", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - }, - "windows-amd64": { - "path": "lib/win64_vc15", - "branch": "tags/blender-2.93-release", - "commit_id": "HEAD" - } - } - } - }, - "buildbot": { - "gcc": { - "version": "9.0" - }, - "sdks": { - "optix": { - "version": "7.1.0" - }, - "cuda10": { - "version": "10.1" - }, - "cuda11": { - "version": "11.3" - } - }, - "cmake": { - "default": { - "version": "any", - "overrides": {} - }, - "darwin-x86_64": { - "overrides": {} - }, - "darwin-arm64": { - "overrides": {} - }, - "linux-x86_64": { - "overrides": {} - }, - "windows-amd64": { - "overrides": {} - } - } - } -} \ No newline at end of file -- cgit v1.2.3 From 2161840d07ae0a934a734ac6198aa7e5cc4b6fcf Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 31 May 2021 10:20:23 +0200 Subject: Fix (studio-reported) crash in collection management code. Code checking for potential collection loop dependencies can be called in cases where we cannot guarantee that there is no NULL pointers, so we need to check those. Was already done for objects. NOTE: doubled-checked by @jbakker, thanks. --- source/blender/blenkernel/intern/collection.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 3170c3aa65c..2813f535eb5 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1425,7 +1425,8 @@ static bool collection_instance_find_recursive(Collection *collection, } LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { - if (collection_instance_find_recursive(collection_child->collection, instance_collection)) { + if (collection_child->collection != NULL && + collection_instance_find_recursive(collection_child->collection, instance_collection)) { return true; } } -- cgit v1.2.3 From ce649c73446ea710ffdf7383495b52b0827a5893 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 31 May 2021 11:25:12 +0200 Subject: Fix T88623, T87044: Make encoded videos play correctly in VLC The issue was two fold. We didn't properly: 1. Initialize the codec default values which would lead to VLC complaining because of garbage/wrong codec settings. 2.Calculate the time base for the video. FFmpeg would happily accept this but VLC seems to assume the time base value is at least somewhat correct and couldn't properly display the frames as the internal time base was huge. We are talking about 90k ticks (tbn) for one second of video! This patch initializes all codecs to use their default values and fixes the time base calculation so it follows the guidelines from ffmpeg. Reviewed By: Sergey, Richard Antalik Differential Revision: http://developer.blender.org/D11426 --- source/blender/blenkernel/intern/writeffmpeg.c | 175 ++++++++++++++----------- source/blender/imbuf/intern/indexer.c | 14 +- 2 files changed, 103 insertions(+), 86 deletions(-) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 3408f990fd1..7c8eba20a28 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -518,6 +518,48 @@ static void set_ffmpeg_properties(RenderData *rd, } } +static AVRational calc_time_base(uint den, double num, int codec_id) +{ + /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer + * (within a floating point error range). + * For example if we have den = 3 and num = 0.1 then the fps is: den/num = 30 fps. + * When converthing this to a ffmpeg time base, we want num to be an integer. + * So we simply move the decimal places of both numbers. IE den = 30, num = 1.*/ + float eps = FLT_EPSILON; + const uint DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; + + /* Calculate the precision of the initial floating point number. */ + if (num > 1.0) { + const uint num_integer_bits = log2_floor_u((unsigned int)num); + + /* Formula for calculating the epsilon value: (power of two range) / (pow mantissa bits) + * For example, a float has 23 manitissa bits and the float value 3.5f as a pow2 range of + * (4-2=2): + * (2) / pow2(23) = floating point precision for 3.5f + */ + eps = (float)(1 << num_integer_bits) * FLT_EPSILON; + } + + /* Calculate how many decimal shifts we can do until we run out of precision. */ + const int max_num_shift = fabsf(log10f(eps)); + /* Calculate how many times we can shift the denominator. */ + const int max_den_shift = log10f(DENUM_MAX) - log10f(den); + const int max_iter = min_ii(max_num_shift, max_den_shift); + + for (int i = 0; i < max_iter && fabs(num - round(num)) > eps; i++) { + /* Increase the number and denominator until both are integers. */ + num *= 10; + den *= 10; + eps *= 10; + } + + AVRational time_base; + time_base.den = den; + time_base.num = (int)num; + + return time_base; +} + /* prepare a video stream for the output file */ static AVStream *alloc_video_stream(FFMpegContext *context, @@ -548,13 +590,24 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_VIDEO; + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "Couldn't find valid video codec\n"); + avcodec_free_context(&c); + context->video_codec = NULL; + return NULL; + } + + /* Load codec defaults into 'c'. */ + avcodec_get_context_defaults3(c, codec); + /* Get some values from the current render settings */ c->width = rectx; c->height = recty; - /* FIXME: Really bad hack (tm) for NTSC support */ if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { + /* FIXME: Really bad hack (tm) for NTSC support */ c->time_base.den = 2997; c->time_base.num = 100; } @@ -562,21 +615,23 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->time_base.den = rd->frs_sec; c->time_base.num = (int)rd->frs_sec_base; } - else if (compare_ff(rd->frs_sec_base, 1.001f, 0.000001f)) { - /* This converts xx/1.001 (which is used in presets) to xx000/1001 (which is used in the rest - * of the world, including FFmpeg). */ - c->time_base.den = (int)(rd->frs_sec * 1000); - c->time_base.num = (int)(rd->frs_sec_base * 1000); - } else { - /* This calculates a fraction (DENUM_MAX / num) which approximates the scene frame rate - * (frs_sec / frs_sec_base). It uses the maximum denominator allowed by FFmpeg. - */ - const double DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; - const double num = (DENUM_MAX / (double)rd->frs_sec) * rd->frs_sec_base; + c->time_base = calc_time_base(rd->frs_sec, rd->frs_sec_base, codec_id); + } - c->time_base.den = (int)DENUM_MAX; - c->time_base.num = (int)num; + /* As per the timebase documentation here: + * https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options + * We want to set the time base to (1 / fps) for fixed frame rate video. + * If it is not possible, we want to set the timebase numbers to something as + * small as possible. + */ + if (c->time_base.num != 1) { + AVRational new_time_base; + if (av_reduce( + &new_time_base.num, &new_time_base.den, c->time_base.num, c->time_base.den, INT_MAX)) { + /* Exact reduction was possible. Use the new value. */ + c->time_base = new_time_base; + } } st->time_base = c->time_base; @@ -588,6 +643,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context, ffmpeg_dict_set_int(&opts, "lossless", 1); } else if (context->ffmpeg_crf >= 0) { + /* As per https://trac.ffmpeg.org/wiki/Encode/VP9 we must set the bit rate to zero when + * encoding with vp9 in crf mode. + * Set this to always be zero for other codecs as well. + * We don't care about bit rate in crf mode. */ + c->bit_rate = 0; ffmpeg_dict_set_int(&opts, "crf", context->ffmpeg_crf); } else { @@ -627,12 +687,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - avcodec_free_context(&c); - return NULL; - } - /* Be sure to use the correct pixel format(e.g. RGB, YUV) */ if (codec->pix_fmts) { @@ -649,12 +703,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X'); } - if (codec_id == AV_CODEC_ID_H264) { - /* correct wrong default ffmpeg param which crash x264 */ - c->qmin = 10; - c->qmax = 51; - } - /* Keep lossless encodes in the RGB domain. */ if (codec_id == AV_CODEC_ID_HUFFYUV) { if (rd->im_format.planes == R_IMF_PLANES_RGBA) { @@ -714,10 +762,14 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->thread_type = FF_THREAD_SLICE; } - if (avcodec_open2(c, codec, &opts) < 0) { + int ret = avcodec_open2(c, codec, &opts); + + if (ret < 0) { + fprintf(stderr, "Couldn't initialize video codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); avcodec_free_context(&c); + context->video_codec = NULL; return NULL; } av_dict_free(&opts); @@ -777,6 +829,17 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_AUDIO; + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "Couldn't find valid audio codec\n"); + avcodec_free_context(&c); + context->audio_codec = NULL; + return NULL; + } + + /* Load codec defaults into 'c'. */ + avcodec_get_context_defaults3(c, codec); + c->sample_rate = rd->ffcodecdata.audio_mixrate; c->bit_rate = context->ffmpeg_audio_bitrate * 1000; c->sample_fmt = AV_SAMPLE_FMT_S16; @@ -806,13 +869,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->sample_fmt = AV_SAMPLE_FMT_FLT; } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - // XXX error("Couldn't find a valid audio codec"); - avcodec_free_context(&c); - return NULL; - } - if (codec->sample_fmts) { /* Check if the preferred sample format for this codec is supported. * this is because, depending on the version of libav, @@ -852,11 +908,14 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, set_ffmpeg_properties(rd, c, "audio", &opts); - if (avcodec_open2(c, codec, &opts) < 0) { - // XXX error("Couldn't initialize audio codec"); + int ret = avcodec_open2(c, codec, &opts); + + if (ret < 0) { + fprintf(stderr, "Couldn't initialize audio codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); avcodec_free_context(&c); + context->audio_codec = NULL; return NULL; } av_dict_free(&opts); @@ -1636,49 +1695,7 @@ static void ffmpeg_set_expert_options(RenderData *rd) IDP_FreePropertyContent(rd->ffcodecdata.properties); } - if (codec_id == AV_CODEC_ID_H264) { - /* - * All options here are for x264, but must be set via ffmpeg. - * The names are therefore different - Search for "x264 to FFmpeg option mapping" - * to get a list. - */ - - /* - * Use CABAC coder. Using "coder:1", which should be equivalent, - * crashes Blender for some reason. Either way - this is no big deal. - */ - BKE_ffmpeg_property_add_string(rd, "video", "coder:vlc"); - - /* - * The other options were taken from the libx264-default.preset - * included in the ffmpeg distribution. - */ - - /* This breaks compatibility for QT. */ - // BKE_ffmpeg_property_add_string(rd, "video", "flags:loop"); - BKE_ffmpeg_property_add_string(rd, "video", "cmp:chroma"); - BKE_ffmpeg_property_add_string(rd, "video", "partitions:parti4x4"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partp8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partb8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "me:hex"); - BKE_ffmpeg_property_add_string(rd, "video", "subq:6"); - BKE_ffmpeg_property_add_string(rd, "video", "me_range:16"); - BKE_ffmpeg_property_add_string(rd, "video", "qdiff:4"); - BKE_ffmpeg_property_add_string(rd, "video", "keyint_min:25"); - BKE_ffmpeg_property_add_string(rd, "video", "sc_threshold:40"); - BKE_ffmpeg_property_add_string(rd, "video", "i_qfactor:0.71"); - BKE_ffmpeg_property_add_string(rd, "video", "b_strategy:1"); - BKE_ffmpeg_property_add_string(rd, "video", "bf:3"); - BKE_ffmpeg_property_add_string(rd, "video", "refs:2"); - BKE_ffmpeg_property_add_string(rd, "video", "qcomp:0.6"); - - BKE_ffmpeg_property_add_string(rd, "video", "trellis:0"); - BKE_ffmpeg_property_add_string(rd, "video", "weightb:1"); - BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1"); - BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1"); - BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2"); - } - else if (codec_id == AV_CODEC_ID_DNXHD) { + if (codec_id == AV_CODEC_ID_DNXHD) { if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd"); } diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 453df6078ce..e1e6cc677ed 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -492,13 +492,6 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->c = avcodec_alloc_context3(NULL); rv->c->codec_type = AVMEDIA_TYPE_VIDEO; rv->c->codec_id = AV_CODEC_ID_H264; - rv->c->width = width; - rv->c->height = height; - rv->c->gop_size = 10; - rv->c->max_b_frames = 0; - /* Correct wrong default ffmpeg param which crash x264. */ - rv->c->qmin = 10; - rv->c->qmax = 51; rv->of->oformat->video_codec = rv->c->codec_id; rv->codec = avcodec_find_encoder(rv->c->codec_id); @@ -513,6 +506,13 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( return NULL; } + avcodec_get_context_defaults3(rv->c, rv->codec); + + rv->c->width = width; + rv->c->height = height; + rv->c->gop_size = 10; + rv->c->max_b_frames = 0; + if (rv->codec->pix_fmts) { rv->c->pix_fmt = rv->codec->pix_fmts[0]; } -- cgit v1.2.3 From e9f2f17e8518f31706756b4ebe7c38a3582a3612 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Mon, 31 May 2021 12:26:46 +0200 Subject: Fix (unreported): TextureOperation inputs have no resolution When compositor node tree has a texture node, TextureOperation vector inputs has always {0, 0} resolution instead of having same resolution as TextureOperation which is the expected behaviour for resolutions propagation. Current TextureOperation determineResolution implementation doesn't determine inputs resolution, breaking propagation of preferred resolution and that's the reason why they are always 0. Setting scene resolution always would mean it is its own resolution and could make sense, but setting it only when preferred resolution is 0, breaks preferred resolution logic affecting other operations as explained in D10972. In any case scene resolution is already the default preferred resolution on viewer and compositor nodes. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D11381 --- .../compositor/operations/COM_TextureOperation.cc | 17 +++++++---------- .../compositor/operations/COM_TextureOperation.h | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index e94c457f981..7517ff8a137 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -75,16 +75,13 @@ void TextureBaseOperation::deinitExecution() void TextureBaseOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { - if (preferredResolution[0] == 0 || preferredResolution[1] == 0) { - int width = this->m_rd->xsch * this->m_rd->size / 100; - int height = this->m_rd->ysch * this->m_rd->size / 100; - resolution[0] = width; - resolution[1] = height; - } - else { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; - } + /* Determine inputs resolutions. */ + unsigned int temp[2]; + NodeOperation::determineResolution(temp, preferredResolution); + + /* We don't use inputs resolutions because they are only used as parameters, not image data. */ + resolution[0] = preferredResolution[0]; + resolution[1] = preferredResolution[1]; } void TextureAlphaOperation::executePixelSampled(float output[4], diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index e1e04611c6c..e5f56673694 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -44,7 +44,7 @@ class TextureBaseOperation : public NodeOperation { protected: /** - * Determine the output resolution. The resolution is retrieved from the Renderer + * Determine the output resolution. */ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; -- cgit v1.2.3 From e0a1c3da46ada31058471a0c40bcc8fd56929846 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 14:32:39 +0200 Subject: Fix T88666: Cryptomatte: EXR sequence does not update when scrubbing the timeline. Cause is that initializing the cryptomatte session would reset the current frame of an image sequence. The solution is to always use the scene current frame so it resets to the correct frame. This was a todo that wasn't solved after it landed in master. Needs to be backported to 2.93. --- source/blender/blenkernel/BKE_node.h | 11 +- .../compositor/nodes/COM_CryptomatteNode.cc | 8 +- .../editors/interface/interface_eyedropper_color.c | 98 +++++++++------- source/blender/makesrna/intern/rna_nodetree.c | 6 +- .../composite/nodes/node_composite_cryptomatte.cc | 124 ++++++++++++--------- 5 files changed, 143 insertions(+), 104 deletions(-) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index b3247a751bf..fb6647cb68d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1305,15 +1305,18 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteSyncFromAdd(bNode *node); +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node); void ntreeCompositCryptomatteSyncFromRemove(bNode *node); bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node); int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len); +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len); /* Update the runtime layer names with the cryptomatte layer names of the references * render layer or image. */ -void ntreeCompositCryptomatteUpdateLayerNames(bNode *node); -struct CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node); +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); +struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); /** \} */ diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index d97e4371dc6..3beb3aa2917 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -77,10 +77,10 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, /** \name Cryptomatte V2 * \{ */ -static std::string prefix_from_node(const bNode &node) +static std::string prefix_from_node(const CompositorContext &context, const bNode &node) { char prefix[MAX_NAME]; - ntreeCompositCryptomatteLayerPrefix(&node, prefix, sizeof(prefix)); + ntreeCompositCryptomatteLayerPrefix(context.getScene(), &node, prefix, sizeof(prefix)); return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix))); } @@ -119,7 +119,7 @@ void CryptomatteNode::input_operations_from_render_source( } const short cryptomatte_layer_id = 0; - const std::string prefix = prefix_from_node(node); + const std::string prefix = prefix_from_node(context, node); LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name); if (render_layer) { @@ -177,7 +177,7 @@ void CryptomatteNode::input_operations_from_image_source( } } - const std::string prefix = prefix_from_node(node); + const std::string prefix = prefix_from_node(context, node); int layer_index; LISTBASE_FOREACH_INDEX (RenderLayer *, render_layer, &image->rr->layers, layer_index) { if (!blender::StringRef(prefix).startswith(blender::StringRef( diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index d5fb0e4e744..dd8b1e6a23e 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -118,7 +118,8 @@ static bool eyedropper_init(bContext *C, wmOperator *op) RNA_property_float_get_array(&eye->ptr, eye->prop, col); if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) { eye->crypto_node = (bNode *)eye->ptr.data; - eye->cryptomatte_session = ntreeCompositCryptomatteSession(eye->crypto_node); + eye->cryptomatte_session = ntreeCompositCryptomatteSession(CTX_data_scene(C), + eye->crypto_node); eye->draw_handle_sample_text = WM_draw_cb_activate(CTX_wm_window(C), eyedropper_draw_cb, eye); } @@ -199,6 +200,57 @@ static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_lay return false; } +static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Scene *scene = (Scene *)node->id; + BLI_assert(GS(scene->id.name) == ID_SCE); + Render *re = RE_GetSceneRender(scene); + + if (re) { + RenderResult *rr = RE_AcquireResultRead(re); + if (rr) { + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { + RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + RE_ReleaseResult(re); + } + return success; +} + +static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node, + NodeCryptomatte *crypto, + const char *prefix, + const float fpos[2], + float r_col[3]) +{ + bool success = false; + Image *image = (Image *)node->id; + BLI_assert(GS(image->id.name) == ID_IM); + ImageUser *iuser = &crypto->iuser; + + if (image && image->type == IMA_TYPE_MULTILAYER) { + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); + if (image->rr) { + LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { + success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col); + if (success) { + break; + } + } + } + BKE_image_release_ibuf(image, ibuf, NULL); + } + return success; +} static bool eyedropper_cryptomatte_sample_fl( bContext *C, Eyedropper *eye, int mx, int my, float r_col[3]) @@ -255,53 +307,19 @@ static bool eyedropper_cryptomatte_sample_fl( return false; } - bool success = false; /* TODO(jbakker): Migrate this file to cc and use std::string as return param. */ char prefix[MAX_NAME + 1]; - ntreeCompositCryptomatteLayerPrefix(node, prefix, sizeof(prefix) - 1); + const Scene *scene = CTX_data_scene(C); + ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1); prefix[MAX_NAME] = '\0'; if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { - Scene *scene = (Scene *)node->id; - BLI_assert(GS(scene->id.name) == ID_SCE); - Render *re = RE_GetSceneRender(scene); - - if (re) { - RenderResult *rr = RE_AcquireResultRead(re); - if (rr) { - LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { - RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name); - success = eyedropper_cryptomatte_sample_renderlayer_fl( - render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - RE_ReleaseResult(re); - } + return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col); } else if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { - Image *image = (Image *)node->id; - BLI_assert(GS(image->id.name) == ID_IM); - ImageUser *iuser = &crypto->iuser; - - if (image && image->type == IMA_TYPE_MULTILAYER) { - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); - if (image->rr) { - LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) { - success = eyedropper_cryptomatte_sample_renderlayer_fl( - render_layer, prefix, fpos, r_col); - if (success) { - break; - } - } - } - BKE_image_release_ibuf(image, ibuf, NULL); - } + return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col); } - - return success; + return false; } /** diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b3ba111dc61..0ca275eb58b 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -3973,7 +3973,7 @@ static void rna_NodeCryptomatte_layer_name_set(PointerRNA *ptr, int new_value) } } -static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UNUSED(C), +static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) @@ -3984,7 +3984,7 @@ static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UN EnumPropertyItem template = {0, "", 0, "", ""}; int totitem = 0; - ntreeCompositCryptomatteUpdateLayerNames(node); + ntreeCompositCryptomatteUpdateLayerNames(CTX_data_scene(C), node); int layer_index; LISTBASE_FOREACH_INDEX (CryptomatteLayer *, layer, &storage->runtime.layers, layer_index) { template.value = layer_index; @@ -4076,7 +4076,7 @@ static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value) static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr) { - ntreeCompositCryptomatteSyncFromAdd(ptr->data); + ntreeCompositCryptomatteSyncFromAdd(scene, ptr->data); rna_Node_update(bmain, scene, ptr); } diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index d9b36924516..dca6dc59ca2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -40,61 +40,74 @@ /** \name Cryptomatte * \{ */ +static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render( + const bNode &node, const bool use_meta_data) +{ + blender::bke::cryptomatte::CryptomatteSessionPtr session; + + Scene *scene = (Scene *)node.id; + if (!scene) { + return session; + } + BLI_assert(GS(scene->id.name) == ID_SCE); + + if (use_meta_data) { + Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr; + RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr; + if (render_result) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_render_result(render_result)); + } + if (render) { + RE_ReleaseResult(render); + } + } + + if (session == nullptr) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_scene(scene)); + } + return session; +} + +static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_image( + const Scene &scene, const bNode &node) +{ + blender::bke::cryptomatte::CryptomatteSessionPtr session; + Image *image = (Image *)node.id; + if (!image) { + return session; + } + BLI_assert(GS(image->id.name) == ID_IM); + + NodeCryptomatte *node_cryptomatte = static_cast(node.storage); + ImageUser *iuser = &node_cryptomatte->iuser; + BKE_image_user_frame_calc(image, iuser, scene.r.cfra); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); + RenderResult *render_result = image->rr; + if (render_result) { + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_render_result(render_result)); + } + BKE_image_release_ibuf(image, ibuf, nullptr); + return session; +} static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node( - const bNode &node, const int frame_number, const bool use_meta_data) + const Scene &scene, const bNode &node, const bool use_meta_data) { blender::bke::cryptomatte::CryptomatteSessionPtr session; if (node.type != CMP_NODE_CRYPTOMATTE) { return session; } - NodeCryptomatte *node_cryptomatte = static_cast(node.storage); switch (node.custom1) { case CMP_CRYPTOMATTE_SRC_RENDER: { - Scene *scene = (Scene *)node.id; - if (!scene) { - return session; - } - BLI_assert(GS(scene->id.name) == ID_SCE); - - if (use_meta_data) { - Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr; - RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr; - if (render_result) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_render_result(render_result)); - } - if (render) { - RE_ReleaseResult(render); - } - } - - if (session == nullptr) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_scene(scene)); - } - - break; + return cryptomatte_init_from_node_render(node, use_meta_data); } case CMP_CRYPTOMATTE_SRC_IMAGE: { - Image *image = (Image *)node.id; - if (!image) { - break; - } - BLI_assert(GS(image->id.name) == ID_IM); - - ImageUser *iuser = &node_cryptomatte->iuser; - BKE_image_user_frame_calc(image, iuser, frame_number); - ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); - RenderResult *render_result = image->rr; - if (render_result) { - session = blender::bke::cryptomatte::CryptomatteSessionPtr( - BKE_cryptomatte_init_from_render_result(render_result)); - } - BKE_image_release_ibuf(image, ibuf, nullptr); - break; + return cryptomatte_init_from_node_image(scene, node); } } return session; @@ -111,7 +124,10 @@ static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encode return nullptr; } -static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, float encoded_hash) +static void cryptomatte_add(const Scene &scene, + bNode &node, + NodeCryptomatte &node_cryptomatte, + float encoded_hash) { /* Check if entry already exist. */ if (cryptomatte_find(node_cryptomatte, encoded_hash)) { @@ -121,9 +137,8 @@ static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, floa CryptomatteEntry *entry = static_cast( MEM_callocN(sizeof(CryptomatteEntry), __func__)); entry->encoded_hash = encoded_hash; - /* TODO(jbakker): Get current frame from scene. */ blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - node, 0, true); + scene, node, true); if (session) { BKE_cryptomatte_find_name(session.get(), encoded_hash, entry->name, sizeof(entry->name)); } @@ -151,12 +166,12 @@ static bNodeSocketTemplate cmp_node_cryptomatte_out[] = { {-1, ""}, }; -void ntreeCompositCryptomatteSyncFromAdd(bNode *node) +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node) { BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY)); NodeCryptomatte *n = static_cast(node->storage); if (n->runtime.add[0] != 0.0f) { - cryptomatte_add(*node, *n, n->runtime.add[0]); + cryptomatte_add(*scene, *node, *n, n->runtime.add[0]); zero_v3(n->runtime.add); } } @@ -170,14 +185,14 @@ void ntreeCompositCryptomatteSyncFromRemove(bNode *node) zero_v3(n->runtime.remove); } } -void ntreeCompositCryptomatteUpdateLayerNames(bNode *node) +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node) { BLI_assert(node->type == CMP_NODE_CRYPTOMATTE); NodeCryptomatte *n = static_cast(node->storage); BLI_freelistN(&n->runtime.layers); blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - *node, 0, false); + *scene, *node, false); if (session) { for (blender::StringRef layer_name : @@ -190,12 +205,15 @@ void ntreeCompositCryptomatteUpdateLayerNames(bNode *node) } } -void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len) +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len) { BLI_assert(node->type == CMP_NODE_CRYPTOMATTE); NodeCryptomatte *node_cryptomatte = (NodeCryptomatte *)node->storage; blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( - *node, 0, false); + *scene, *node, false); std::string first_layer_name; if (session) { @@ -216,10 +234,10 @@ void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size BLI_strncpy(r_prefix, cstr, prefix_len); } -CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node) +CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node) { blender::bke::cryptomatte::CryptomatteSessionPtr session_ptr = cryptomatte_init_from_node( - *node, 0, true); + *scene, *node, true); return session_ptr.release(); } -- cgit v1.2.3 From d94ba979d890e0a89a3ff9fa5ac8676f389533b5 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 31 May 2021 13:17:23 +0200 Subject: Fix T88569: UI VSE: Menu-based range change, doesn't update the Timeline scrollbar width Use the appropriate notifier, listeners were already doing the rest properly. Maniphest Tasks: T88569 Differential Revision: https://developer.blender.org/D11436 --- source/blender/editors/space_sequencer/sequencer_edit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index ebd4c0090b4..53ddc818cd1 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -3053,7 +3053,7 @@ static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op) scene->r.efra = efra; } - WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + WM_event_add_notifier(C, NC_SCENE | ND_FRAME_RANGE, scene); return OPERATOR_FINISHED; } -- cgit v1.2.3 From d67c13ca761a865a208d6c9ad4ee2504aa577e94 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 31 May 2021 15:15:37 +0200 Subject: Cleanup: else-after-return --- source/blender/editors/interface/interface_eyedropper_color.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index dd8b1e6a23e..6583fa31dbf 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -316,7 +316,7 @@ static bool eyedropper_cryptomatte_sample_fl( if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col); } - else if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { + if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) { return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col); } return false; -- cgit v1.2.3 From aebeb85fe013a671f71f47d6f210c192598f408f Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Mon, 31 May 2021 08:56:57 -0600 Subject: Windows: Clean-up win 8/8.1 API use For 2.93 we bumped the minimum windows requirement to windows 8.1, but did not do any clean-up of any win 8/8.1 API usage we dynamically accessed though LoadLibrary/GetProcAddress. This patch bumps _WIN32_WINNT to 0x0603 (win 8.1) and cleans up any API use that was accessed in a more convoluted way than necessary Differential Revision: https://developer.blender.org/D11331 Reviewed by: harley, nicholas_rishel --- build_files/cmake/platform/platform_win32.cmake | 6 +- intern/ghost/intern/GHOST_SystemWin32.cpp | 73 +--------- intern/ghost/intern/GHOST_WindowWin32.cpp | 35 +---- intern/ghost/intern/GHOST_WindowWin32.h | 168 ----------------------- release/windows/manifest/blender.exe.manifest.in | 6 - 5 files changed, 9 insertions(+), 279 deletions(-) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index b6ec016aa95..497fd179aaf 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -119,7 +119,7 @@ string(APPEND CMAKE_MODULE_LINKER_FLAGS " /SAFESEH:NO /ignore:4099") list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi - pathcch + pathcch Shcore ) if(WITH_INPUT_IME) @@ -144,8 +144,8 @@ add_definitions(-D_ALLOW_KEYWORD_MACROS) # that both /GR and /GR- are specified. remove_cc_flag("/GR") -# We want to support Windows 7 level ABI -add_definitions(-D_WIN32_WINNT=0x601) +# Make the Windows 8.1 API available for use. +add_definitions(-D_WIN32_WINNT=0x603) include(build_files/cmake/platform/platform_win32_bundle_crt.cmake) remove_cc_flag("/MDd" "/MD" "/Zi") diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 38dea9c1142..fc3cb81e26a 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -97,41 +98,6 @@ # define VK_GR_LESS 0xE2 #endif // VK_GR_LESS -#ifndef VK_MEDIA_NEXT_TRACK -# define VK_MEDIA_NEXT_TRACK 0xB0 -#endif // VK_MEDIA_NEXT_TRACK -#ifndef VK_MEDIA_PREV_TRACK -# define VK_MEDIA_PREV_TRACK 0xB1 -#endif // VK_MEDIA_PREV_TRACK -#ifndef VK_MEDIA_STOP -# define VK_MEDIA_STOP 0xB2 -#endif // VK_MEDIA_STOP -#ifndef VK_MEDIA_PLAY_PAUSE -# define VK_MEDIA_PLAY_PAUSE 0xB3 -#endif // VK_MEDIA_PLAY_PAUSE - -// Window message newer than Windows 7 -#ifndef WM_DPICHANGED -# define WM_DPICHANGED 0x02E0 -#endif // WM_DPICHANGED - -// WM_POINTER API messages minimum Windows 7 -#ifndef WM_POINTERENTER -# define WM_POINTERENTER 0x0249 -#endif // WM_POINTERENTER -#ifndef WM_POINTERDOWN -# define WM_POINTERDOWN 0x0246 -#endif // WM_POINTERDOWN -#ifndef WM_POINTERUPDATE -# define WM_POINTERUPDATE 0x0245 -#endif // WM_POINTERUPDATE -#ifndef WM_POINTERUP -# define WM_POINTERUP 0x0247 -#endif // WM_POINTERUP -#ifndef WM_POINTERLEAVE -# define WM_POINTERLEAVE 0x024A -#endif // WM_POINTERLEAVE - /* Workaround for some laptop touchpads, some of which seems to * have driver issues which makes it so window function receives * the message, but PeekMessage doesn't pick those messages for @@ -173,24 +139,6 @@ static void initRawInput() #undef DEVICE_COUNT } -#ifndef DPI_ENUMS_DECLARED -typedef enum PROCESS_DPI_AWARENESS { - PROCESS_DPI_UNAWARE = 0, - PROCESS_SYSTEM_DPI_AWARE = 1, - PROCESS_PER_MONITOR_DPI_AWARE = 2 -} PROCESS_DPI_AWARENESS; - -typedef enum MONITOR_DPI_TYPE { - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI -} MONITOR_DPI_TYPE; - -# define USER_DEFAULT_SCREEN_DPI 96 - -# define DPI_ENUMS_DECLARED -#endif typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND); @@ -205,15 +153,7 @@ GHOST_SystemWin32::GHOST_SystemWin32() // Tell Windows we are per monitor DPI aware. This disables the default // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. - HMODULE m_shcore = ::LoadLibrary("Shcore.dll"); - if (m_shcore) { - GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness = - (GHOST_WIN32_SetProcessDpiAwareness)::GetProcAddress(m_shcore, "SetProcessDpiAwareness"); - - if (fpSetProcessDpiAwareness) { - fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - } - } + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); // Check if current keyboard layout uses AltGr and save keylayout ID for // specialized handling if keys like VK_OEM_*. I.e. french keylayout @@ -581,14 +521,7 @@ GHOST_TSuccess GHOST_SystemWin32::init() InitCommonControls(); /* Disable scaling on high DPI displays on Vista */ - HMODULE - user32 = ::LoadLibraryA("user32.dll"); - typedef BOOL(WINAPI * LPFNSETPROCESSDPIAWARE)(); - LPFNSETPROCESSDPIAWARE SetProcessDPIAware = (LPFNSETPROCESSDPIAWARE)GetProcAddress( - user32, "SetProcessDPIAware"); - if (SetProcessDPIAware) - SetProcessDPIAware(); - FreeLibrary(user32); + SetProcessDPIAware(); initRawInput(); m_lfstart = ::GetTickCount(); diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 3af92153e8b..eeafe333633 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -84,9 +84,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_wantAlphaBackground(alphaBackground), m_normal_state(GHOST_kWindowStateNormal), m_user32(NULL), - m_fpGetPointerInfoHistory(NULL), - m_fpGetPointerPenInfoHistory(NULL), - m_fpGetPointerTouchInfoHistory(NULL), m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), m_debug_context(is_debug) { @@ -153,19 +150,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, m_user32 = ::LoadLibrary("user32.dll"); if (m_hWnd) { - if (m_user32) { - // Touch enabled screens with pen support by default have gestures - // enabled, which results in a delay between the pointer down event - // and the first move when using the stylus. RegisterTouchWindow - // disables the new gesture architecture enabling the events to be - // sent immediately to the application rather than being absorbed by - // the gesture API. - GHOST_WIN32_RegisterTouchWindow pRegisterTouchWindow = (GHOST_WIN32_RegisterTouchWindow) - GetProcAddress(m_user32, "RegisterTouchWindow"); - if (pRegisterTouchWindow) { - pRegisterTouchWindow(m_hWnd, 0); - } - } + RegisterTouchWindow(m_hWnd, 0); // Register this window as a droptarget. Requires m_hWnd to be valid. // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32. @@ -232,16 +217,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, } } - // Initialize Windows Ink - if (m_user32) { - m_fpGetPointerInfoHistory = (GHOST_WIN32_GetPointerInfoHistory)::GetProcAddress( - m_user32, "GetPointerInfoHistory"); - m_fpGetPointerPenInfoHistory = (GHOST_WIN32_GetPointerPenInfoHistory)::GetProcAddress( - m_user32, "GetPointerPenInfoHistory"); - m_fpGetPointerTouchInfoHistory = (GHOST_WIN32_GetPointerTouchInfoHistory)::GetProcAddress( - m_user32, "GetPointerTouchInfoHistory"); - } - // Initialize Wintab m_wintab.handle = ::LoadLibrary("Wintab32.dll"); if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) { @@ -326,9 +301,6 @@ GHOST_WindowWin32::~GHOST_WindowWin32() if (m_user32) { FreeLibrary(m_user32); m_user32 = NULL; - m_fpGetPointerInfoHistory = NULL; - m_fpGetPointerPenInfoHistory = NULL; - m_fpGetPointerTouchInfoHistory = NULL; } if (m_customCursor) { @@ -950,15 +922,14 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); GHOST_TUns32 outCount; - if (!(m_fpGetPointerInfoHistory && m_fpGetPointerInfoHistory(pointerId, &outCount, NULL))) { + if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) { return GHOST_kFailure; } auto pointerPenInfo = std::vector(outCount); outPointerInfo.resize(outCount); - if (!(m_fpGetPointerPenInfoHistory && - m_fpGetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { + if (!(GetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) { return GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index b004f7e7b19..a13bd876667 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -53,177 +53,12 @@ typedef BOOL(API *GHOST_WIN32_WTPacket)(HCTX, UINT, LPVOID); typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL); typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL); -// typedef to user32 functions to disable gestures on windows -typedef BOOL(API *GHOST_WIN32_RegisterTouchWindow)(HWND hwnd, ULONG ulFlags); - // typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND); #ifndef USER_DEFAULT_SCREEN_DPI # define USER_DEFAULT_SCREEN_DPI 96 #endif // USER_DEFAULT_SCREEN_DPI -// typedefs for user32 functions to allow pointer functions -enum tagPOINTER_INPUT_TYPE { - PT_POINTER = 1, // Generic pointer - PT_TOUCH = 2, // Touch - PT_PEN = 3, // Pen - PT_MOUSE = 4, // Mouse -#if (WINVER >= 0x0603) - PT_TOUCHPAD = 5, // Touchpad -#endif /* WINVER >= 0x0603 */ -}; - -typedef enum tagPOINTER_BUTTON_CHANGE_TYPE { - POINTER_CHANGE_NONE, - POINTER_CHANGE_FIRSTBUTTON_DOWN, - POINTER_CHANGE_FIRSTBUTTON_UP, - POINTER_CHANGE_SECONDBUTTON_DOWN, - POINTER_CHANGE_SECONDBUTTON_UP, - POINTER_CHANGE_THIRDBUTTON_DOWN, - POINTER_CHANGE_THIRDBUTTON_UP, - POINTER_CHANGE_FOURTHBUTTON_DOWN, - POINTER_CHANGE_FOURTHBUTTON_UP, - POINTER_CHANGE_FIFTHBUTTON_DOWN, - POINTER_CHANGE_FIFTHBUTTON_UP, -} POINTER_BUTTON_CHANGE_TYPE; - -typedef DWORD POINTER_INPUT_TYPE; -typedef UINT32 POINTER_FLAGS; - -#define POINTER_FLAG_NONE 0x00000000 -#define POINTER_FLAG_NEW 0x00000001 -#define POINTER_FLAG_INRANGE 0x00000002 -#define POINTER_FLAG_INCONTACT 0x00000004 -#define POINTER_FLAG_FIRSTBUTTON 0x00000010 -#define POINTER_FLAG_SECONDBUTTON 0x00000020 -#define POINTER_FLAG_THIRDBUTTON 0x00000040 -#define POINTER_FLAG_FOURTHBUTTON 0x00000080 -#define POINTER_FLAG_FIFTHBUTTON 0x00000100 -#define POINTER_FLAG_PRIMARY 0x00002000 -#define POINTER_FLAG_CONFIDENCE 0x000004000 -#define POINTER_FLAG_CANCELED 0x000008000 -#define POINTER_FLAG_DOWN 0x00010000 -#define POINTER_FLAG_UPDATE 0x00020000 -#define POINTER_FLAG_UP 0x00040000 -#define POINTER_FLAG_WHEEL 0x00080000 -#define POINTER_FLAG_HWHEEL 0x00100000 -#define POINTER_FLAG_CAPTURECHANGED 0x00200000 -#define POINTER_FLAG_HASTRANSFORM 0x00400000 - -typedef struct tagPOINTER_INFO { - POINTER_INPUT_TYPE pointerType; - UINT32 pointerId; - UINT32 frameId; - POINTER_FLAGS pointerFlags; - HANDLE sourceDevice; - HWND hwndTarget; - POINT ptPixelLocation; - POINT ptHimetricLocation; - POINT ptPixelLocationRaw; - POINT ptHimetricLocationRaw; - DWORD dwTime; - UINT32 historyCount; - INT32 InputData; - DWORD dwKeyStates; - UINT64 PerformanceCount; - POINTER_BUTTON_CHANGE_TYPE ButtonChangeType; -} POINTER_INFO; - -typedef UINT32 PEN_FLAGS; -#define PEN_FLAG_NONE 0x00000000 // Default -#define PEN_FLAG_BARREL 0x00000001 // The barrel button is pressed -#define PEN_FLAG_INVERTED 0x00000002 // The pen is inverted -#define PEN_FLAG_ERASER 0x00000004 // The eraser button is pressed - -typedef UINT32 PEN_MASK; -#define PEN_MASK_NONE 0x00000000 // Default - none of the optional fields are valid -#define PEN_MASK_PRESSURE 0x00000001 // The pressure field is valid -#define PEN_MASK_ROTATION 0x00000002 // The rotation field is valid -#define PEN_MASK_TILT_X 0x00000004 // The tiltX field is valid -#define PEN_MASK_TILT_Y 0x00000008 // The tiltY field is valid - -typedef struct tagPOINTER_PEN_INFO { - POINTER_INFO pointerInfo; - PEN_FLAGS penFlags; - PEN_MASK penMask; - UINT32 pressure; - UINT32 rotation; - INT32 tiltX; - INT32 tiltY; -} POINTER_PEN_INFO; - -/* - * Flags that appear in pointer input message parameters - */ -#define POINTER_MESSAGE_FLAG_NEW 0x00000001 // New pointer -#define POINTER_MESSAGE_FLAG_INRANGE 0x00000002 // Pointer has not departed -#define POINTER_MESSAGE_FLAG_INCONTACT 0x00000004 // Pointer is in contact -#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010 // Primary action -#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020 // Secondary action -#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040 // Third button -#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080 // Fourth button -#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100 // Fifth button -#define POINTER_MESSAGE_FLAG_PRIMARY 0x00002000 // Pointer is primary -#define POINTER_MESSAGE_FLAG_CONFIDENCE \ - 0x00004000 // Pointer is considered unlikely to be accidental -#define POINTER_MESSAGE_FLAG_CANCELED 0x00008000 // Pointer is departing in an abnormal manner - -typedef UINT32 TOUCH_FLAGS; -#define TOUCH_FLAG_NONE 0x00000000 // Default - -typedef UINT32 TOUCH_MASK; -#define TOUCH_MASK_NONE 0x00000000 // Default - none of the optional fields are valid -#define TOUCH_MASK_CONTACTAREA 0x00000001 // The rcContact field is valid -#define TOUCH_MASK_ORIENTATION 0x00000002 // The orientation field is valid -#define TOUCH_MASK_PRESSURE 0x00000004 // The pressure field is valid - -typedef struct tagPOINTER_TOUCH_INFO { - POINTER_INFO pointerInfo; - TOUCH_FLAGS touchFlags; - TOUCH_MASK touchMask; - RECT rcContact; - RECT rcContactRaw; - UINT32 orientation; - UINT32 pressure; -} POINTER_TOUCH_INFO; - -/* - * Macros to retrieve information from pointer input message parameters - */ -#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam)) -#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag)) -#define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_NEW) -#define IS_POINTER_INRANGE_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INRANGE) -#define IS_POINTER_INCONTACT_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INCONTACT) -#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON) -#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON) -#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON) -#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON) -#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON) -#define IS_POINTER_PRIMARY_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_PRIMARY) -#define HAS_POINTER_CONFIDENCE_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CONFIDENCE) -#define IS_POINTER_CANCELED_WPARAM(wParam) \ - IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED) - -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_INFO *pointerInfo); -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerPenInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_PEN_INFO *penInfo); -typedef BOOL(WINAPI *GHOST_WIN32_GetPointerTouchInfoHistory)(UINT32 pointerId, - UINT32 *entriesCount, - POINTER_TOUCH_INFO *touchInfo); - struct GHOST_PointerInfoWin32 { GHOST_TInt32 pointerId; GHOST_TInt32 isPrimary; @@ -576,9 +411,6 @@ class GHOST_WindowWin32 : public GHOST_Window { /** `user32.dll` handle */ HMODULE m_user32; - GHOST_WIN32_GetPointerInfoHistory m_fpGetPointerInfoHistory; - GHOST_WIN32_GetPointerPenInfoHistory m_fpGetPointerPenInfoHistory; - GHOST_WIN32_GetPointerTouchInfoHistory m_fpGetPointerTouchInfoHistory; HWND m_parentWindowHwnd; diff --git a/release/windows/manifest/blender.exe.manifest.in b/release/windows/manifest/blender.exe.manifest.in index e73ddf3267b..b516efe24cb 100644 --- a/release/windows/manifest/blender.exe.manifest.in +++ b/release/windows/manifest/blender.exe.manifest.in @@ -13,12 +13,6 @@ - - - - - - -- cgit v1.2.3 From 44d2479dc36fe3409afc660eea369bea8a517987 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 31 May 2021 17:11:01 +0200 Subject: Refactor: DRW Mesh Extractor: Join the extractors in a same loop This patch replaces / redoes the entire MeshExtractors system. Although they were useful and facilitated the addition of new buffers, they made it difficult to control the threads and added a lot of threading overhead. Part of the problem was in traversing the same loop type in different threads. The concurrent access of the BMesh Elements slowed the reading. This patch simplifies the use of threads by merging all the old callbacks from the extracts into a single series of iteration functions. The type of extraction can be chosen using flags. This optimized the process by around 34%. Initial idea and implementation By @mano-wii. Fine-tuning, cleanup by @atmind. MASTER: large_mesh_editing: - rdata 9ms iter 50ms (frame 155ms) - Average: 6.462874 FPS PATCH: large_mesh_editing: - rdata 9ms iter 34ms (frame 136ms) - Average: 7.379491 FPS Differential Revision: https://developer.blender.org/D11425 --- source/blender/draw/intern/draw_cache_extract.h | 2 +- .../blender/draw/intern/draw_cache_extract_mesh.c | 2756 +++++++++++--------- .../blender/draw/intern/draw_cache_impl_curve.cc | 6 + .../blender/draw/intern/draw_cache_impl_displist.c | 4 + source/blender/draw/intern/draw_cache_impl_mesh.c | 6 +- source/blender/draw/intern/draw_cache_inline.h | 4 - 6 files changed, 1527 insertions(+), 1251 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 14cbba82fba..e6b7fb9ddf5 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -286,7 +286,7 @@ typedef struct MeshBatchCache { void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, - MeshBufferCache mbc, + MeshBufferCache *mbc, MeshBufferExtractionCache *extraction_cache, Mesh *me, const bool is_editmode, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 62d8040e88b..1a78ce71d74 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -78,6 +78,13 @@ # include "PIL_time_utildefines.h" #endif +#define CHUNK_SIZE 8192 + +/* + * Max number of extractions types. + */ +#define M_EXTRACT_LEN 38 + /* ---------------------------------------------------------------------- */ /** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). * \{ */ @@ -528,7 +535,8 @@ typedef struct ExtractTriBMesh_Params { int tri_range[2]; } ExtractTriBMesh_Params; typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, - const ExtractTriBMesh_Params *params, + BMLoop **elt, + const int elt_index, void *data); #define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ @@ -545,7 +553,8 @@ typedef struct ExtractTriMesh_Params { int tri_range[2]; } ExtractTriMesh_Params; typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, - const ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int elt_index, void *data); #define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ @@ -568,7 +577,8 @@ typedef struct ExtractPolyBMesh_Params { int poly_range[2]; } ExtractPolyBMesh_Params; typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int f_index, void *data); #define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ @@ -613,7 +623,8 @@ typedef struct ExtractPolyMesh_Params { int poly_range[2]; } ExtractPolyMesh_Params; typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data); #define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ @@ -662,7 +673,8 @@ typedef struct ExtractLEdgeBMesh_Params { int ledge_range[2]; } ExtractLEdgeBMesh_Params; typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data); #define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ @@ -687,7 +699,8 @@ typedef struct ExtractLEdgeMesh_Params { int ledge_range[2]; } ExtractLEdgeMesh_Params; typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *data); #define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ @@ -717,7 +730,8 @@ typedef struct ExtractLVertBMesh_Params { int lvert_range[2]; } ExtractLVertBMesh_Params; typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *data); #define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ @@ -742,7 +756,8 @@ typedef struct ExtractLVertMesh_Params { int lvert_range[2]; } ExtractLVertMesh_Params; typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *data); #define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ @@ -793,8 +808,98 @@ typedef struct MeshExtract { const eMRDataType data_flag; /** Used to know if the element callbacks are thread-safe and can be parallelized. */ const bool use_threading; + /** + * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index + * buffer. + */ + const size_t mesh_buffer_offset; } MeshExtract; +static void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) +{ + /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to + * `MeshBufferCache *`. What shows a different usage versus intent. */ + void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset); + void *buffer = *buffer_ptr; + BLI_assert(buffer); + return buffer; +} + +typedef struct MeshExtractRunData { + const MeshExtract *extractor; + void *buffer; + void *user_data; +} MeshExtractRunData; + +typedef struct MeshExtractRunDataArray { + int len; + MeshExtractRunData items[M_EXTRACT_LEN]; +} MeshExtractRunDataArray; + +static void mesh_extract_run_data_array_init(MeshExtractRunDataArray *array) +{ + array->len = 0; +} + +static void mesh_extract_run_data_array_add_ex(MeshExtractRunDataArray *array, + const MeshExtractRunData *run_data) +{ + array->items[array->len] = *run_data; + array->len++; +} + +static void mesh_extract_run_data_array_add(MeshExtractRunDataArray *array, + const MeshExtract *extractor) +{ + MeshExtractRunData run_data; + run_data.extractor = extractor; + run_data.buffer = NULL; + run_data.user_data = NULL; + mesh_extract_run_data_array_add_ex(array, &run_data); +} + +static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray *src, + MeshExtractRunDataArray *dst, + eMRIterType iter_type) +{ + for (int i = 0; i < src->len; i++) { + + const MeshExtractRunData *data = &src->items[i]; + const MeshExtract *extractor = data->extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + } +} + +static void mesh_extract_run_data_array_filter_threading( + const MeshExtractRunDataArray *src, MeshExtractRunDataArray *dst_multi_threaded) +{ + for (int i = 0; i < src->len; i++) { + const MeshExtract *extractor = src->items[i].extractor; + if (extractor->use_threading) { + mesh_extract_run_data_array_add(dst_multi_threaded, extractor); + } + } +} + BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext) { eMRIterType type = 0; @@ -866,69 +971,65 @@ static void *extract_tris_init(const MeshRenderData *mr, } static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *_data) { MeshExtract_Tri_Data *data = _data; const int mat_last = mr->mat_len - 1; - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(elt[0]->f->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts(&data->elb, - mat_tri_ofs[mat]++, - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } + + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(elt[0]->f->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts(&data->elb, + mat_tri_ofs[mat]++, + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; } static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *_data) { MeshExtract_Tri_Data *data = _data; const int mat_last = mr->mat_len - 1; - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(mp->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts( - &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(mp->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts( + &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } static void extract_tris_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_Tri_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch * is created before the surfaces-per-material. */ if (mr->use_final_mesh && cache->final.tris_per_mat) { - MeshBufferCache *mbc = &cache->final; + MeshBufferCache *mbc_final = &cache->final; for (int i = 0; i < mr->mat_len; i++) { /* These IBOs have not been queried yet but we create them just in case they are needed * later since they are not tracked by mesh_buffer_cache_create_requested(). */ - if (mbc->tris_per_mat[i] == NULL) { - mbc->tris_per_mat[i] = GPU_indexbuf_calloc(); + if (mbc_final->tris_per_mat[i] == NULL) { + mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); } /* Multiply by 3 because these are triangle indices. */ const int mat_start = data->tri_mat_start[i]; const int mat_end = data->tri_mat_end[i]; const int start = mat_start * 3; const int len = (mat_end - mat_start) * 3; - GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, start, len); + GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); } } MEM_freeN(data->tri_mat_start); @@ -943,7 +1044,7 @@ static const MeshExtract extract_tris = { .finish = extract_tris_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; /** \} */ @@ -962,120 +1063,104 @@ static void *extract_lines_init(const MeshRenderData *mr, return elb; } -static void extract_lines_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *elb) { - /* Using poly & loop iterator would complicate accessing the adjacent loop. */ - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - BMLoop *l_iter, *l_first; - /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ - l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; - do { - if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_line_verts(elb, - BM_elem_index_get(l_iter->e), - BM_elem_index_get(l_iter), - BM_elem_index_get(l_iter->next)); - } - else { - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); - } - } while ((l_iter = l_iter->next) != l_first); - } - EXTRACT_POLY_FOREACH_BM_END; + BMLoop *l_iter, *l_first; + /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; + do { + if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_line_verts(elb, + BM_elem_index_get(l_iter->e), + BM_elem_index_get(l_iter), + BM_elem_index_get(l_iter->next)); + } + else { + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); + } + } while ((l_iter = l_iter->next) != l_first); } static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *elb) { /* Using poly & loop iterator would complicate accessing the adjacent loop. */ const MLoop *mloop = mr->mloop; const MEdge *medge = mr->medge; if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - const MEdge *med = &medge[ml->e]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } - else { - GPU_indexbuf_set_line_restart(elb, ml->e); - } - } while ((ml_index = ml_index_next++) != ml_index_last); - } - EXTRACT_POLY_FOREACH_MESH_END; + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + const MEdge *med = &medge[ml->e]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } + else { + GPU_indexbuf_set_line_restart(elb, ml->e); + } + } while ((ml_index = ml_index_next++) != ml_index_last); } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } while ((ml_index = ml_index_next++) != ml_index_last); - } - EXTRACT_POLY_FOREACH_MESH_END; + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } while ((ml_index = ml_index_next++) != ml_index_last); } } static void extract_lines_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - const int l_index_offset = mr->edge_len + ledge_index; - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); + const int l_index_offset = mr->edge_len + ledge_index; + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); } - EXTRACT_LEDGE_FOREACH_BM_END; + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); } static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int l_index_offset = mr->edge_len + ledge_index; - const int e_index = mr->ledges[ledge_index]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, e_index); + const int l_index_offset = mr->edge_len + ledge_index; + const int e_index = mr->ledges[ledge_index]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); } - EXTRACT_LEDGE_FOREACH_MESH_END; + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, e_index); } static void extract_lines_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } @@ -1089,7 +1174,8 @@ static const MeshExtract extract_lines = { .finish = extract_lines_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -1109,9 +1195,10 @@ static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshB static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); extract_lines_loose_subbuffer(mr, cache); MEM_freeN(elb); @@ -1126,7 +1213,7 @@ static const MeshExtract extract_lines_with_lines_loose = { .finish = extract_lines_with_lines_loose_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; /** \} */ @@ -1170,81 +1257,76 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, } } -static void extract_points_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *elb) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - vert_set_bm(elb, l->v, l_index); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + vert_set_bm(elb, l_iter->v, l_index); + } while ((l_iter = l_iter->next) != l_first); } static void extract_points_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *elb) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; vert_set_mesh(elb, mr, ml->v, ml_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_points_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); - vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); - } - EXTRACT_LEDGE_FOREACH_BM_END; + vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); + vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); } static void extract_points_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *elb) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); - vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); - } - EXTRACT_LEDGE_FOREACH_MESH_END; + vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); + vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); } static void extract_points_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *elb) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - vert_set_bm(elb, eve, offset + lvert_index); - } - EXTRACT_LVERT_FOREACH_BM_END; + vert_set_bm(elb, eve, offset + lvert_index); } static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *elb) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); - } - EXTRACT_LVERT_FOREACH_MESH_END; + + vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); } static void extract_points_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } @@ -1260,7 +1342,7 @@ static const MeshExtract extract_points = { .finish = extract_points_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; /** \} */ @@ -1277,59 +1359,54 @@ static void *extract_fdots_init(const MeshRenderData *mr, return elb; } -static void extract_fdots_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *elb) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_point_vert(elb, f_index, f_index); - } - else { - GPU_indexbuf_set_point_restart(elb, f_index); - } + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, f_index, f_index); + } + else { + GPU_indexbuf_set_point_restart(elb, f_index); } - EXTRACT_POLY_FOREACH_BM_END; } static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *elb) { if (mr->use_subsurf_fdots) { /* Check #ME_VERT_FACEDOT. */ - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; const MVert *mv = &mr->mvert[ml->v]; if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); + return; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; + GPU_indexbuf_set_point_restart(elb, mp_index); } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); - } + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + } + else { + GPU_indexbuf_set_point_restart(elb, mp_index); } - EXTRACT_POLY_FOREACH_MESH_END; } } static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *elb) { + GPUIndexBuf *ibo = buf; GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); } @@ -1341,7 +1418,7 @@ static const MeshExtract extract_fdots = { .finish = extract_fdots_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; /** \} */ @@ -1357,7 +1434,7 @@ typedef struct MeshExtract_LinePaintMask_Data { static void *extract_lines_paint_mask_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) + void *UNUSED(ibo)) { size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); @@ -1366,12 +1443,16 @@ static void *extract_lines_paint_mask_init(const MeshRenderData *mr, } static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_LinePaintMask_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const int e_index = ml->e; const MEdge *me = &mr->medge[e_index]; if (!((mr->use_hide && (me->flag & ME_HIDE)) || @@ -1401,15 +1482,15 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, GPU_indexbuf_set_line_restart(&data->elb, e_index); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } + static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_LinePaintMask_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); } @@ -1420,7 +1501,7 @@ static const MeshExtract extract_lines_paint_mask = { .finish = extract_lines_paint_mask_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; /** \} */ @@ -1498,49 +1579,44 @@ BLI_INLINE void lines_adjacency_triangle( } static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), - BM_elem_index_get(elt[1]->v), - BM_elem_index_get(elt[2]->v), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2]), - data); - } + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), + BM_elem_index_get(elt[1]->v), + BM_elem_index_get(elt[2]->v), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2]), + data); } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; } static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, - mr->mloop[mlt->tri[1]].v, - mr->mloop[mlt->tri[2]].v, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2], - data); - } + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *cache, - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_LineAdjacency_Data *data = _data; /* Create edges for remaining non manifold edges. */ EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); @@ -1577,7 +1653,7 @@ static const MeshExtract extract_lines_adjacency = { .finish = extract_lines_adjacency_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; /** \} */ @@ -1609,43 +1685,38 @@ BLI_INLINE void edituv_tri_add( } static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - const struct ExtractTriBMesh_Params *params, + BMLoop **elt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params) - { - edituv_tri_add(data, - BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; + edituv_tri_add(data, + BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); } static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, - const struct ExtractTriMesh_Params *params, + const MLoopTri *mlt, + const int UNUSED(elt_index), void *data) { - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params) - { - const MPoly *mp = &mr->mpoly[mlt->poly]; - edituv_tri_add(data, - (mp->flag & ME_HIDE) != 0, - (mp->flag & ME_FACE_SEL) != 0, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2]); - } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; + const MPoly *mp = &mr->mpoly[mlt->poly]; + edituv_tri_add(data, + (mp->flag & ME_HIDE) != 0, + (mp->flag & ME_FACE_SEL) != 0, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2]); } static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); @@ -1658,7 +1729,7 @@ static const MeshExtract extract_edituv_tris = { .finish = extract_edituv_tris_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; /** \} */ @@ -1685,27 +1756,34 @@ BLI_INLINE void edituv_edge_add( } } -static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + edituv_edge_add(data, - BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(loop->f, BM_ELEM_SELECT), + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), l_index, - BM_elem_index_get(loop->next)); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop); + BM_elem_index_get(l_iter->next)); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const int ml_index_last = mp->totloop + mp->loopstart - 1; const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE); @@ -1715,14 +1793,14 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, ml_index, ml_index_next); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); @@ -1735,7 +1813,7 @@ static const MeshExtract extract_edituv_lines = { .finish = extract_edituv_lines_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; /** \} */ @@ -1764,39 +1842,44 @@ BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, } } -static void extract_edituv_points_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - edituv_point_add(data, - BM_elem_flag_test(l->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(l->f, BM_ELEM_SELECT), - l_index); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_point_add( + data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && mr->v_origindex[ml->v] != ORIGINDEX_NONE); edituv_point_add( data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); MEM_freeN(extract_data); @@ -1809,7 +1892,7 @@ static const MeshExtract extract_edituv_points = { .finish = extract_edituv_points_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; /** \} */ @@ -1841,26 +1924,29 @@ BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, } } -static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *data) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - edituv_facedot_add( - data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), f_index); - } - EXTRACT_POLY_FOREACH_BM_END; + edituv_facedot_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + f_index); } static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { if (mr->use_subsurf_fdots) { /* Check #ME_VERT_FACEDOT. */ - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && mr->p_origindex[mp_index] != ORIGINDEX_NONE); const bool subd_fdot = (!mr->use_subsurf_fdots || @@ -1870,27 +1956,21 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, (mp->flag & ME_FACE_SEL) != 0, mp_index); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - edituv_facedot_add(data, - ((mp->flag & ME_HIDE) != 0) || !real_fdot, - (mp->flag & ME_FACE_SEL) != 0, - mp_index); - } - EXTRACT_POLY_FOREACH_MESH_END; + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + edituv_facedot_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); } } static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *ibo, + void *buf, void *_data) { + GPUIndexBuf *ibo = buf; MeshExtract_EditUvElem_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); @@ -1903,7 +1983,7 @@ static const MeshExtract extract_edituv_fdots = { .finish = extract_edituv_fdots_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; /** \} */ @@ -1925,6 +2005,7 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING Adjust #PosNorLoop struct accordingly. */ @@ -1932,7 +2013,6 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "vnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -1960,28 +2040,34 @@ static void *extract_pos_nor_init(const MeshRenderData *mr, } static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v)); - vert->nor = data->normals[BM_elem_index_get(l->v)].low; - BMFace *efa = l->f; - vert->nor.w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low; + vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); } static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); @@ -1999,79 +2085,69 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, vert->nor.w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - int l_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; - vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; - } - EXTRACT_LEDGE_FOREACH_BM_END; + + int l_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; + vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; } static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { MeshExtract_PosNor_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - vert[0].nor = data->normals[med->v1].low; - vert[1].nor = data->normals[med->v2].low; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + vert[0].nor = data->normals[med->v1].low; + vert[1].nor = data->normals[med->v2].low; } static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { MeshExtract_PosNor_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - const int l_index = offset + lvert_index; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - vert->nor = data->normals[BM_elem_index_get(eve)].low; - } - EXTRACT_LVERT_FOREACH_BM_END; + + const int l_index = offset + lvert_index; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + vert->nor = data->normals[BM_elem_index_get(eve)].low; } static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *_data) { MeshExtract_PosNor_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - vert->nor = data->normals[v_index].low; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + vert->nor = data->normals[v_index].low; } static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *UNUSED(vbo), + void *UNUSED(buf), void *data) { MEM_freeN(data); @@ -2088,7 +2164,8 @@ static const MeshExtract extract_pos_nor = { .finish = extract_pos_nor_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -2109,6 +2186,7 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING Adjust #PosNorHQLoop struct accordingly. */ @@ -2116,7 +2194,6 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "vnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -2144,29 +2221,35 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr, } static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l->v)].high); + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high); - BMFace *efa = l->f; + BMFace *efa = l_iter->f; vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); @@ -2185,85 +2268,74 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, vert->nor[3] = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - int l_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); - vert[1].nor[3] = 0; - } - EXTRACT_LEDGE_FOREACH_BM_END; + int l_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); + vert[1].nor[3] = 0; } static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); - vert[1].nor[3] = 0; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); + vert[1].nor[3] = 0; } static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - const int l_index = offset + lvert_index; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); - vert->nor[3] = 0; - } - EXTRACT_LVERT_FOREACH_BM_END; + + const int l_index = offset + lvert_index; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); + vert->nor[3] = 0; } static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *mv, + const int lvert_index, void *_data) { MeshExtract_PosNorHQ_Data *data = _data; const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - copy_v3_v3_short(vert->nor, data->normals[v_index].high); - vert->nor[3] = 0; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + copy_v3_v3_short(vert->nor, data->normals[v_index].high); + vert->nor[3] = 0; } static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr), struct MeshBatchCache *UNUSED(cache), - void *UNUSED(vbo), + void *UNUSED(buf), void *data) { MEM_freeN(data); @@ -2280,7 +2352,7 @@ static const MeshExtract extract_pos_nor_hq = { .finish = extract_pos_nor_hq_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -2295,12 +2367,12 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "lnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2308,36 +2380,37 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr, } static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *data) { - if (mr->loop_normals) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(_l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(_l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l->v)); + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); } else { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, l->f)); + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f)); } } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + } while ((l_iter = l_iter->next) != l_first); } static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index]; if (mr->loop_normals) { normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]); @@ -2363,7 +2436,6 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, lnor_data->w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static const MeshExtract extract_lnor_hq = { @@ -2372,7 +2444,7 @@ static const MeshExtract extract_lnor_hq = { .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, .data_flag = MR_DATA_LOOP_NOR, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -2383,12 +2455,12 @@ static void *extract_lnor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); GPU_vertformat_alias_add(&format, "lnor"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2396,40 +2468,39 @@ static void *extract_lnor_init(const MeshRenderData *mr, } static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *data) { - if (mr->loop_normals) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); - BMFace *efa = l->f; - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, l->v)); + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3( + bm_vert_no_get(mr, l_iter->v)); } else { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, l->f)); + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); } - BMFace *efa = l->f; - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); } static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index]; if (mr->loop_normals) { *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]); @@ -2455,7 +2526,6 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, lnor_data->w = 0; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static const MeshExtract extract_lnor = { @@ -2464,7 +2534,7 @@ static const MeshExtract extract_lnor = { .iter_poly_mesh = extract_lnor_iter_poly_mesh, .data_flag = MR_DATA_LOOP_NOR, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; /** \} */ @@ -2474,6 +2544,7 @@ static const MeshExtract extract_lnor = { static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); @@ -2523,7 +2594,6 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca v_len = 1; } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, v_len); @@ -2556,11 +2626,10 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca return NULL; } -static const MeshExtract extract_uv = { - .init = extract_uv_init, - .data_flag = 0, - .use_threading = false, -}; +static const MeshExtract extract_uv = {.init = extract_uv_init, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; /** \} */ @@ -2568,10 +2637,10 @@ static const MeshExtract extract_uv = { /** \name Extract Tangent layers * \{ */ -static void extract_tan_ex(const MeshRenderData *mr, - struct MeshBatchCache *cache, - GPUVertBuf *vbo, - const bool do_hq) +static void extract_tan_ex_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertBuf *vbo, + const bool do_hq) { GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; @@ -2742,15 +2811,15 @@ static void extract_tan_ex(const MeshRenderData *mr, static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, cache, buf, false); + extract_tan_ex_init(mr, cache, buf, false); return NULL; } -static const MeshExtract extract_tan = { - .init = extract_tan_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, - .use_threading = false, -}; +static const MeshExtract extract_tan = {.init = extract_tan_init, + .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | + MR_DATA_LOOPTRI, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; /** \} */ @@ -2760,7 +2829,7 @@ static const MeshExtract extract_tan = { static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, cache, buf, true); + extract_tan_ex_init(mr, cache, buf, true); return NULL; } @@ -2780,6 +2849,7 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; @@ -2794,7 +2864,6 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -2868,7 +2937,7 @@ static const MeshExtract extract_sculpt_data = { .data_flag = 0, /* TODO: enable threading. */ .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; /** \} */ @@ -2878,6 +2947,7 @@ static const MeshExtract extract_sculpt_data = { static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); @@ -2939,7 +3009,6 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * } } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3016,7 +3085,7 @@ static const MeshExtract extract_vcol = { .init = extract_vcol_init, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; /** \} */ @@ -3033,6 +3102,7 @@ static void *extract_orco_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex @@ -3042,7 +3112,6 @@ static void *extract_orco_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3056,32 +3125,36 @@ static void *extract_orco_init(const MeshRenderData *mr, return data; } -static void extract_orco_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); float *loop_orco = orco_data->vbo_data[l_index]; - copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]); + copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]); loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop); + } while ((l_iter = l_iter->next) != l_first); } static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; float *loop_orco = orco_data->vbo_data[ml_index]; copy_v3_v3(loop_orco, orco_data->orco[ml->v]); loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_orco_finish(const MeshRenderData *UNUSED(mr), @@ -3099,7 +3172,7 @@ static const MeshExtract extract_orco = { .finish = extract_orco_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; /** \} */ @@ -3135,11 +3208,12 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -3170,43 +3244,47 @@ static void *extract_edge_fac_init(const MeshRenderData *mr, } static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - if (BM_edge_is_manifold(l->e)) { - float ratio = loop_edge_factor_get(bm_face_no_get(mr, l->f), - bm_vert_co_get(mr, l->v), - bm_vert_no_get(mr, l->v), - bm_vert_co_get(mr, l->next->v)); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + if (BM_edge_is_manifold(l_iter->e)) { + float ratio = loop_edge_factor_get(bm_face_no_get(mr, f), + bm_vert_co_get(mr, l_iter->v), + bm_vert_no_get(mr, l_iter->v), + bm_vert_co_get(mr, l_iter->next->v)); data->vbo_data[l_index] = ratio * 253 + 1; } else { data->vbo_data[l_index] = 255; } - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; - if (data->use_edge_render) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + if (data->use_edge_render) { const MEdge *med = &mr->medge[ml->e]; data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { + /* Count loop per edge to detect non-manifold. */ if (data->edge_loop_count[ml->e] < 3) { data->edge_loop_count[ml->e]++; @@ -3228,34 +3306,28 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, data->vbo_data[ml_index] = 255; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *UNUSED(eed), + const int ledge_index, void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; - data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; - } - EXTRACT_LEDGE_FOREACH_BM_END; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; } static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *UNUSED(med), + const uint ledge_index, void *_data) { MeshExtract_EdgeFac_Data *data = _data; - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; - data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + + data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; + data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; } static void extract_edge_fac_finish(const MeshRenderData *mr, @@ -3263,10 +3335,10 @@ static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data) { + GPUVertBuf *vbo = buf; MeshExtract_EdgeFac_Data *data = _data; if (GPU_crappy_amd_driver()) { - GPUVertBuf *vbo = (GPUVertBuf *)buf; /* Some AMD drivers strangely crash with VBO's with a one byte format. * To workaround we reinitialize the VBO with another format and convert * all bytes to floats. */ @@ -3301,7 +3373,7 @@ static const MeshExtract extract_edge_fac = { .finish = extract_edge_fac_finish, .data_flag = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -3372,11 +3444,11 @@ static void *extract_weights_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); @@ -3400,48 +3472,44 @@ static void *extract_weights_init(const MeshRenderData *mr, return data; } -static void extract_weights_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_Weight_Data *data = _data; - if (data->cd_ofs != -1) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l->v, data->cd_ofs); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (data->cd_ofs != -1) { + const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs); data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + else { data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); - } + } while ((l_iter = l_iter->next) != l_first); } static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_Weight_Data *data = _data; - if (data->dvert != NULL) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (data->dvert != NULL) { const MDeformVert *dvert = &data->dvert[ml->v]; data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - const MDeformVert *dvert = NULL; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { + const MDeformVert *dvert = NULL; data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } @@ -3460,7 +3528,7 @@ static const MeshExtract extract_weights = { .finish = extract_weights_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; /** \} */ @@ -3610,40 +3678,45 @@ static void *extract_edit_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING: Adjust #EditLoopData struct accordingly. */ GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); GPU_vertformat_alias_add(&format, "flag"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); return GPU_vertbuf_get_data(vbo); } static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { EditLoopData *data = (EditLoopData *)_data + l_index; memset(data, 0x0, sizeof(*data)); - mesh_render_data_face_flag(mr, l->f, -1, data); - mesh_render_data_edge_flag(mr, l->e, data); - mesh_render_data_vert_flag(mr, l->v, data); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + mesh_render_data_face_flag(mr, f, -1, data); + mesh_render_data_edge_flag(mr, l_iter->e, data); + mesh_render_data_vert_flag(mr, l_iter->v, data); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; EditLoopData *data = (EditLoopData *)_data + ml_index; memset(data, 0x0, sizeof(*data)); BMFace *efa = bm_original_face_get(mr, mp_index); @@ -3659,81 +3732,69 @@ static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, mesh_render_data_vert_flag(mr, eve, data); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *_data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); - memset(data, 0x0, sizeof(*data) * 2); - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - mesh_render_data_vert_flag(mr, eed->v1, &data[0]); - mesh_render_data_vert_flag(mr, eed->v2, &data[1]); - } - EXTRACT_LEDGE_FOREACH_BM_END; + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); + memset(data, 0x0, sizeof(*data) * 2); + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + mesh_render_data_vert_flag(mr, eed->v1, &data[0]); + mesh_render_data_vert_flag(mr, eed->v2, &data[1]); } static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *_data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; - memset(data, 0x0, sizeof(*data) * 2); - const int e_index = mr->ledges[ledge_index]; - BMEdge *eed = bm_original_edge_get(mr, e_index); - BMVert *eve1 = bm_original_vert_get(mr, med->v1); - BMVert *eve2 = bm_original_vert_get(mr, med->v2); - if (eed) { - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - } - if (eve1) { - mesh_render_data_vert_flag(mr, eve1, &data[0]); - } - if (eve2) { - mesh_render_data_vert_flag(mr, eve2, &data[1]); - } + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; + memset(data, 0x0, sizeof(*data) * 2); + const int e_index = mr->ledges[ledge_index]; + BMEdge *eed = bm_original_edge_get(mr, e_index); + BMVert *eve1 = bm_original_vert_get(mr, med->v1); + BMVert *eve2 = bm_original_vert_get(mr, med->v2); + if (eed) { + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + } + if (eve1) { + mesh_render_data_vert_flag(mr, eve1, &data[0]); + } + if (eve2) { + mesh_render_data_vert_flag(mr, eve2, &data[1]); } - EXTRACT_LEDGE_FOREACH_MESH_END; } static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *_data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - mesh_render_data_vert_flag(mr, eve, data); - } - EXTRACT_LVERT_FOREACH_BM_END; + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_vert_flag(mr, eve, data); } static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *_data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - const int v_index = mr->lverts[lvert_index]; - BMVert *eve = bm_original_vert_get(mr, v_index); - if (eve) { - mesh_render_data_vert_flag(mr, eve, data); - } + + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + const int v_index = mr->lverts[lvert_index]; + BMVert *eve = bm_original_vert_get(mr, v_index); + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); } - EXTRACT_LVERT_FOREACH_MESH_END; } static const MeshExtract extract_edit_data = { @@ -3746,7 +3807,7 @@ static const MeshExtract extract_edit_data = { .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; /** \} */ @@ -3763,6 +3824,7 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* WARNING: Adjust #EditLoopData struct accordingly. */ @@ -3770,7 +3832,6 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, GPU_vertformat_alias_add(&format, "flag"); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3783,28 +3844,34 @@ static void *extract_edituv_data_init(const MeshRenderData *mr, } static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); MeshExtract_EditUVData_Data *data = _data; EditLoopData *eldata = &data->vbo_data[l_index]; memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata); - mesh_render_data_face_flag(mr, l->f, data->cd_ofs, eldata); - mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_EditUVData_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + EditLoopData *eldata = &data->vbo_data[ml_index]; memset(eldata, 0x0, sizeof(*eldata)); BMFace *efa = bm_original_face_get(mr, mp_index); @@ -3834,7 +3901,6 @@ static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), @@ -3852,7 +3918,7 @@ static const MeshExtract extract_edituv_data = { .finish = extract_edituv_data_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; /** \} */ @@ -3864,12 +3930,12 @@ static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -3891,11 +3957,12 @@ BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_t return (ratio > 1.0f) ? (1.0f / ratio) : ratio; } -static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *UNUSED(data)) +static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(data)) { + GPUVertBuf *vbo = buf; float tot_area = 0.0f, tot_uv_area = 0.0f; float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); @@ -3937,7 +4004,6 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, } /* Copy face data for each loop. */ - GPUVertBuf *vbo = buf; uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo); if (mr->extract_type == MR_EXTRACT_BMESH) { @@ -3965,10 +4031,10 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr, static const MeshExtract extract_edituv_stretch_area = { .init = extract_edituv_stretch_area_init, - .finish = mesh_edituv_stretch_area_finish, + .finish = extract_edituv_stretch_area_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; /** \} */ @@ -4034,6 +4100,7 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* Waning: adjust #UVStretchAngle struct accordingly. */ @@ -4041,7 +4108,6 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -4060,21 +4126,24 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, } static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_StretchAngle_Data *data = _data; float(*auv)[2] = data->auv, *last_auv = data->last_auv; float(*av)[3] = data->av, *last_av = data->last_av; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + const MLoopUV *luv, *luv_next; - BMLoop *l_next = l->next; - BMFace *efa = l->f; - if (l == BM_FACE_FIRST_LOOP(efa)) { + BMLoop *l_next = l_iter->next; + if (l_iter == BM_FACE_FIRST_LOOP(f)) { /* First loop in face. */ - BMLoop *l_tmp = l->prev; - BMLoop *l_next_tmp = l; + BMLoop *l_tmp = l_iter->prev; + BMLoop *l_next_tmp = l_iter; luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs); luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs); compute_normalize_edge_vectors(auv, @@ -4087,7 +4156,7 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, copy_v2_v2(last_auv, auv[1]); copy_v3_v3(last_av, av[1]); } - if (l_next == BM_FACE_FIRST_LOOP(efa)) { + if (l_next == BM_FACE_FIRST_LOOP(f)) { /* Move previous edge. */ copy_v2_v2(auv[0], auv[1]); copy_v3_v3(av[0], av[1]); @@ -4096,27 +4165,31 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, copy_v3_v3(av[1], last_av); } else { - luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs); + luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs); - compute_normalize_edge_vectors( - auv, av, luv->uv, luv_next->uv, bm_vert_co_get(mr, l->v), bm_vert_co_get(mr, l_next->v)); + compute_normalize_edge_vectors(auv, + av, + luv->uv, + luv_next->uv, + bm_vert_co_get(mr, l_iter->v), + bm_vert_co_get(mr, l_next->v)); } edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *_data) { MeshExtract_StretchAngle_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { float(*auv)[2] = data->auv, *last_auv = data->last_auv; float(*av)[3] = data->av, *last_av = data->last_av; - int l_next = ml_index + 1, ml_index_end = mp->loopstart + mp->totloop; + int l_next = ml_index + 1; const MVert *v, *v_next; if (ml_index == mp->loopstart) { /* First loop in face. */ @@ -4147,7 +4220,6 @@ static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr } edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr), @@ -4165,7 +4237,7 @@ static const MeshExtract extract_edituv_stretch_angle = { .finish = extract_edituv_stretch_angle_finish, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; /** \} */ @@ -4177,12 +4249,12 @@ static void *extract_mesh_analysis_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -4736,14 +4808,14 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) MEM_freeN(vert_angles); } -static void extract_mesh_analysis_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) +static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) { + GPUVertBuf *vbo = buf; BLI_assert(mr->edit_bmesh); - GPUVertBuf *vbo = buf; float *l_weight = (float *)GPU_vertbuf_get_data(vbo); switch (mr->toolsettings->statvis.type) { @@ -4767,12 +4839,12 @@ static void extract_mesh_analysis_finish(const MeshRenderData *mr, static const MeshExtract extract_mesh_analysis = { .init = extract_mesh_analysis_init, - .finish = extract_mesh_analysis_finish, + .finish = extract_analysis_iter_finish_mesh, /* This is not needed for all visualization types. * * Maybe split into different extract. */ .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; /** \} */ @@ -4784,69 +4856,65 @@ static void *extract_fdots_pos_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); return GPU_vertbuf_get_data(vbo); } static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int f_index, void *data) { float(*center)[3] = data; - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - float *co = center[f_index]; - zero_v3(co); + float *co = center[f_index]; + zero_v3(co); - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); - } while ((l_iter = l_iter->next) != l_first); - mul_v3_fl(co, 1.0f / (float)f->len); - } - EXTRACT_POLY_FOREACH_BM_END; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); + } while ((l_iter = l_iter->next) != l_first); + mul_v3_fl(co, 1.0f / (float)f->len); } static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { float(*center)[3] = (float(*)[3])data; + float *co = center[mp_index]; + zero_v3(co); + const MVert *mvert = mr->mvert; const MLoop *mloop = mr->mloop; - if (mr->use_subsurf_fdots) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { const MVert *mv = &mr->mvert[ml->v]; if (mv->flag & ME_VERT_FACEDOT) { copy_v3_v3(center[mp_index], mv->co); + break; } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - float *co = center[mp_index]; - zero_v3(co); - - const MLoop *ml = &mloop[mp->loopstart]; - for (int i = 0; i < mp->totloop; i++, ml++) { - const MVert *mv = &mvert[ml->v]; - add_v3_v3(center[mp_index], mv->co); - } - mul_v3_fl(co, 1.0f / (float)mp->totloop); + else { + const MVert *mv = &mvert[ml->v]; + add_v3_v3(center[mp_index], mv->co); } - EXTRACT_POLY_FOREACH_MESH_END; + } + + if (!mr->use_subsurf_fdots) { + mul_v3_fl(co, 1.0f / (float)mp->totloop); } } @@ -4856,7 +4924,7 @@ static const MeshExtract extract_fdots_pos = { .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; /** \} */ @@ -4872,11 +4940,12 @@ static void *extract_fdots_nor_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -4888,8 +4957,8 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) { - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); BMFace *efa; @@ -4937,7 +5006,7 @@ static const MeshExtract extract_fdots_nor = { .finish = extract_fdots_nor_finish, .data_flag = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; /** \} */ @@ -4948,11 +5017,12 @@ static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -4964,8 +5034,8 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) { - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; short *nor = (short *)GPU_vertbuf_get_data(vbo); BMFace *efa; @@ -5013,7 +5083,7 @@ static const MeshExtract extract_fdots_nor_hq = { .finish = extract_fdots_nor_hq_finish, .data_flag = MR_DATA_POLY_NOR, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; /** \} */ @@ -5031,13 +5101,14 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "au"); GPU_vertformat_alias_add(&format, "pos"); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -5058,42 +5129,41 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr, return data; } -static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_FdotUV_Data *data = _data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - float w = 1.0f / (float)l->f->len; - const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs); - madd_v2_v2fl(data->vbo_data[BM_elem_index_get(l->f)], luv->uv, w); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + float w = 1.0f / (float)f->len; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); + madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w); + } while ((l_iter = l_iter->next) != l_first); } static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *_data) { MeshExtract_FdotUV_Data *data = _data; - if (mr->use_subsurf_fdots) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { const MVert *mv = &mr->mvert[ml->v]; if (mv->flag & ME_VERT_FACEDOT) { copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv); } } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; - } - else { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + else { float w = 1.0f / (float)mp->totloop; madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w); } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } } @@ -5112,7 +5182,8 @@ static const MeshExtract extract_fdots_uv = { .finish = extract_fdots_uv_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -5128,11 +5199,12 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); @@ -5143,34 +5215,28 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, } static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, + BMFace *f, + const int UNUSED(f_index), void *_data) { MeshExtract_EditUVFdotData_Data *data = _data; - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; - memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); - } - EXTRACT_POLY_FOREACH_BM_END; + EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); } static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *UNUSED(mp), + const int mp_index, void *_data) { MeshExtract_EditUVFdotData_Data *data = _data; - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - EditLoopData *eldata = &data->vbo_data[mp_index]; - memset(eldata, 0x0, sizeof(*eldata)); - BMFace *efa = bm_original_face_get(mr, mp_index); - if (efa) { - mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); - } + EditLoopData *eldata = &data->vbo_data[mp_index]; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, mp_index); + if (efa) { + mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); } - EXTRACT_POLY_FOREACH_MESH_END; } static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), @@ -5188,7 +5254,8 @@ static const MeshExtract extract_fdots_edituv_data = { .finish = extract_fdots_edituv_data_finish, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; + /** \} */ /* ---------------------------------------------------------------------- */ @@ -5204,6 +5271,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; /* Exclusively for edit mode. */ BLI_assert(mr->bm); @@ -5212,7 +5280,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->bm->totvert); @@ -5243,7 +5311,7 @@ static const MeshExtract extract_skin_roots = { .init = extract_skin_roots_init, .data_flag = 0, .use_threading = false, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; /** \} */ @@ -5255,12 +5323,12 @@ static void *extract_select_idx_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* TODO rename "color" to something more descriptive. */ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); return GPU_vertbuf_get_data(vbo); @@ -5271,148 +5339,142 @@ static void *extract_select_idx_init(const MeshRenderData *mr, * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the * shader to output original index. */ -static void extract_poly_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->f); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = f_index; + } while ((l_iter = l_iter->next) != l_first); } -static void extract_edge_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->e); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->e); + } while ((l_iter = l_iter->next) != l_first); } -static void extract_vert_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) - { - ((uint32_t *)data)[l_index] = BM_elem_index_get(l->v); - } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->v); + } while ((l_iter = l_iter->next) != l_first); } static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); - } - EXTRACT_LEDGE_FOREACH_BM_END; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); } static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, + BMEdge *eed, + const int ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); - } - EXTRACT_LEDGE_FOREACH_BM_END; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); } static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, + BMVert *eve, + const int lvert_index, void *data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); - } - EXTRACT_LVERT_FOREACH_BM_END; + + ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); } static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int mp_index, void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *mp, + const int UNUSED(mp_index), void *data) { - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *UNUSED(med), + const uint ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - const int e_index = mr->ledges[ledge_index]; - const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + const int e_index = mr->ledges[ledge_index]; + const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; } static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, + const MEdge *med, + const uint ledge_index, void *data) { - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; - int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; - } - EXTRACT_LEDGE_FOREACH_MESH_END; + int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; + int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; } static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, + const MVert *UNUSED(mv), + const int lvert_index, void *data) { const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EXTRACT_LVERT_FOREACH_MESH_BEGIN(med, lvert_index, params, mr) - { - const int v_index = mr->lverts[lvert_index]; - const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; - ((uint32_t *)data)[offset + lvert_index] = v_orig; - } - EXTRACT_LVERT_FOREACH_MESH_END; + + const int v_index = mr->lverts[lvert_index]; + const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; + ((uint32_t *)data)[offset + lvert_index] = v_orig; } static const MeshExtract extract_poly_idx = { @@ -5421,7 +5483,7 @@ static const MeshExtract extract_poly_idx = { .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; static const MeshExtract extract_edge_idx = { .init = extract_select_idx_init, @@ -5431,7 +5493,7 @@ static const MeshExtract extract_edge_idx = { .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; static const MeshExtract extract_vert_idx = { .init = extract_select_idx_init, @@ -5443,133 +5505,382 @@ static const MeshExtract extract_vert_idx = { .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; -static void *extract_select_fdot_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void *extract_fdot_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { + GPUVertBuf *vbo = buf; static GPUVertFormat format = {0}; if (format.attr_len == 0) { /* TODO rename "color" to something more descriptive. */ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); } - GPUVertBuf *vbo = buf; + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); return GPU_vertbuf_get_data(vbo); } -static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, +static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *UNUSED(f), + const int f_index, void *data) { - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - ((uint32_t *)data)[f_index] = f_index; - } - EXTRACT_POLY_FOREACH_BM_END; + ((uint32_t *)data)[f_index] = f_index; } static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, + const MPoly *UNUSED(mp), + const int mp_index, void *data) { if (mr->p_origindex != NULL) { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; - } - EXTRACT_POLY_FOREACH_MESH_END; + ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; } else { - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - ((uint32_t *)data)[mp_index] = mp_index; - } - EXTRACT_POLY_FOREACH_MESH_END; + ((uint32_t *)data)[mp_index] = mp_index; } } static const MeshExtract extract_fdot_idx = { - .init = extract_select_fdot_idx_init, + .init = extract_fdot_idx_init, .iter_poly_bm = extract_fdot_idx_iter_poly_bm, .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, .data_flag = 0, .use_threading = true, -}; + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; + +/* ---------------------------------------------------------------------- */ +/** \name Extract + * \{ */ + +static void extracts_flags_get(const MeshExtractRunDataArray *extractors, + eMRIterType *r_iter_type, + eMRDataType *r_data_flag) +{ + eMRIterType iter_type = 0; + eMRDataType data_flag = 0; + + for (int i = 0; i < extractors->len; i++) { + const MeshExtract *extractor = extractors->items[i].extractor; + iter_type |= mesh_extract_iter_type(extractor); + data_flag |= extractor->data_flag; + } + + if (r_iter_type) { + *r_iter_type = iter_type; + } + if (r_data_flag) { + *r_data_flag = data_flag; + } +} + +BLI_INLINE void extract_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + MeshBufferCache *mbc) +{ + /* Multi thread. */ + for (int i = 0; i < extractors->len; i++) { + MeshExtractRunData *run_data = &extractors->items[i]; + const MeshExtract *extractor = run_data->extractor; + run_data->buffer = mesh_extract_buffer_get(extractor, mbc); + run_data->user_data = extractor->init(mr, cache, run_data->buffer); + } +} + +BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, + const ExtractTriBMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_looptri_bm(mr, elt, elt_index, run_data->user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, + const ExtractTriMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data->user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, + const ExtractPolyBMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_poly_bm(mr, f, f_index, run_data->user_data); + } + } + EXTRACT_POLY_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, + const ExtractPolyMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_poly_mesh(mr, mp, mp_index, run_data->user_data); + } + } + EXTRACT_POLY_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, + const ExtractLEdgeBMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_ledge_bm(mr, eed, ledge_index, run_data->user_data); + } + } + EXTRACT_LEDGE_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, + const ExtractLEdgeMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_ledge_mesh(mr, med, ledge_index, run_data->user_data); + } + } + EXTRACT_LEDGE_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, + const ExtractLVertBMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_lvert_bm(mr, eve, lvert_index, run_data->user_data); + } + } + EXTRACT_LVERT_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, + const ExtractLVertMesh_Params *params, + const MeshExtractRunDataArray *_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); + mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) + { + for (int i = 0; i < extractors.len; i++) { + MeshExtractRunData *run_data = &extractors.items[i]; + run_data->extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data->user_data); + } + } + EXTRACT_LVERT_FOREACH_MESH_END; +} + +BLI_INLINE void extract_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + const MeshExtractRunDataArray *extractors) +{ + for (int i = 0; i < extractors->len; i++) { + const MeshExtractRunData *run_data = &extractors->items[i]; + const MeshExtract *extractor = run_data->extractor; + if (extractor->finish) { + extractor->finish(mr, cache, run_data->buffer, run_data->user_data); + } + } +} + +/* Single Thread. */ +BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + eMRIterType iter_type, + MeshBufferCache *mbc) +{ + extract_init(mr, cache, extractors, mbc); + + bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH; + if (iter_type & MR_ITER_LOOPTRI) { + if (is_mesh) { + extract_iter_looptri_mesh(mr, + &(const ExtractTriMesh_Params){ + .mlooptri = mr->mlooptri, + .tri_range = {0, mr->tri_len}, + }, + extractors); + } + else { + extract_iter_looptri_bm(mr, + &(const ExtractTriBMesh_Params){ + .looptris = mr->edit_bmesh->looptris, + .tri_range = {0, mr->tri_len}, + }, + extractors); + } + } + if (iter_type & MR_ITER_POLY) { + if (is_mesh) { + extract_iter_poly_mesh(mr, + &(const ExtractPolyMesh_Params){ + .poly_range = {0, mr->poly_len}, + }, + extractors); + } + else { + extract_iter_poly_bm(mr, + &(const ExtractPolyBMesh_Params){ + .poly_range = {0, mr->poly_len}, + }, + extractors); + } + } + if (iter_type & MR_ITER_LEDGE) { + if (is_mesh) { + extract_iter_ledge_mesh(mr, + &(const ExtractLEdgeMesh_Params){ + .ledge = mr->ledges, + .ledge_range = {0, mr->edge_loose_len}, + }, + extractors); + } + else { + extract_iter_ledge_bm(mr, + &(const ExtractLEdgeBMesh_Params){ + .ledge = mr->ledges, + .ledge_range = {0, mr->edge_loose_len}, + }, + extractors); + } + } + if (iter_type & MR_ITER_LVERT) { + if (is_mesh) { + extract_iter_lvert_mesh(mr, + &(const ExtractLVertMesh_Params){ + .lvert = mr->lverts, + .lvert_range = {0, mr->vert_loose_len}, + }, + extractors); + } + else { + extract_iter_lvert_bm(mr, + &(const ExtractLVertBMesh_Params){ + .lvert = mr->lverts, + .lvert_range = {0, mr->vert_loose_len}, + }, + extractors); + } + } + extract_finish(mr, cache, extractors); +} /** \} */ /* ---------------------------------------------------------------------- */ /** \name ExtractTaskData * \{ */ -typedef struct ExtractUserData { - void *user_data; -} ExtractUserData; - -typedef enum ExtractTaskDataType { - EXTRACT_MESH_EXTRACT, - EXTRACT_LINES_LOOSE, -} ExtractTaskDataType; - typedef struct ExtractTaskData { void *next, *prev; const MeshRenderData *mr; struct MeshBatchCache *cache; - const MeshExtract *extract; - ExtractTaskDataType tasktype; + MeshExtractRunDataArray *extractors; eMRIterType iter_type; int start, end; /** Decremented each time a task is finished. */ int32_t *task_counter; - void *buf; - ExtractUserData *user_data; + MeshBufferCache *mbc; } ExtractTaskData; -static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr, - struct MeshBatchCache *cache, - const MeshExtract *extract, - void *buf, - int32_t *task_counter) +static ExtractTaskData *extract_extract_iter_task_data_create_mesh( + const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) { ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), __func__); taskdata->next = NULL; taskdata->prev = NULL; - taskdata->tasktype = EXTRACT_MESH_EXTRACT; taskdata->mr = mr; taskdata->cache = cache; - taskdata->extract = extract; - taskdata->buf = buf; + taskdata->mbc = mbc; - /* #ExtractUserData is shared between the iterations as it holds counters to detect if the + /* #UserData is shared between the iterations as it holds counters to detect if the * extraction is finished. To make sure the duplication of the user_data does not create a new * instance of the counters we allocate the user_data in its own container. * * This structure makes sure that when extract_init is called, that the user data of all * iterations are updated. */ - taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__); - taskdata->iter_type = mesh_extract_iter_type(extract); + taskdata->extractors = extractors; taskdata->task_counter = task_counter; + + extracts_flags_get(extractors, &taskdata->iter_type, NULL); taskdata->start = 0; taskdata->end = INT_MAX; return taskdata; } -static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr, - struct MeshBatchCache *cache) -{ - ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__); - taskdata->tasktype = EXTRACT_LINES_LOOSE; - taskdata->mr = mr; - taskdata->cache = cache; - return taskdata; -} - static void extract_task_data_free(void *data) { ExtractTaskData *task_data = data; - MEM_SAFE_FREE(task_data->user_data); + MEM_SAFE_FREE(task_data->extractors); MEM_freeN(task_data); } @@ -5577,113 +5888,100 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, const eMRIterType iter_type, int start, int end, - const MeshExtract *extract, - void *user_data) + MeshExtractRunDataArray *extractors) { switch (mr->extract_type) { case MR_EXTRACT_BMESH: if (iter_type & MR_ITER_LOOPTRI) { - extract->iter_looptri_bm(mr, - &(const ExtractTriBMesh_Params){ - .looptris = mr->edit_bmesh->looptris, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - user_data); + extract_iter_looptri_bm(mr, + &(const ExtractTriBMesh_Params){ + .looptris = mr->edit_bmesh->looptris, + .tri_range = {start, min_ii(mr->tri_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_POLY) { - extract->iter_poly_bm(mr, - &(const ExtractPolyBMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - user_data); + extract_iter_poly_bm(mr, + &(const ExtractPolyBMesh_Params){ + .poly_range = {start, min_ii(mr->poly_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_LEDGE) { - extract->iter_ledge_bm(mr, - &(const ExtractLEdgeBMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - user_data); + extract_iter_ledge_bm(mr, + &(const ExtractLEdgeBMesh_Params){ + .ledge = mr->ledges, + .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_LVERT) { - extract->iter_lvert_bm(mr, - &(const ExtractLVertBMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - user_data); + extract_iter_lvert_bm(mr, + &(const ExtractLVertBMesh_Params){ + .lvert = mr->lverts, + .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, + }, + extractors); } break; case MR_EXTRACT_MAPPED: case MR_EXTRACT_MESH: if (iter_type & MR_ITER_LOOPTRI) { - extract->iter_looptri_mesh(mr, - &(const ExtractTriMesh_Params){ - .mlooptri = mr->mlooptri, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - user_data); + extract_iter_looptri_mesh(mr, + &(const ExtractTriMesh_Params){ + .mlooptri = mr->mlooptri, + .tri_range = {start, min_ii(mr->tri_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_POLY) { - extract->iter_poly_mesh(mr, - &(const ExtractPolyMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - user_data); + extract_iter_poly_mesh(mr, + &(const ExtractPolyMesh_Params){ + .poly_range = {start, min_ii(mr->poly_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_LEDGE) { - extract->iter_ledge_mesh(mr, - &(const ExtractLEdgeMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - user_data); + extract_iter_ledge_mesh(mr, + &(const ExtractLEdgeMesh_Params){ + .ledge = mr->ledges, + .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, + }, + extractors); } if (iter_type & MR_ITER_LVERT) { - extract->iter_lvert_mesh(mr, - &(const ExtractLVertMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - user_data); + extract_iter_lvert_mesh(mr, + &(const ExtractLVertMesh_Params){ + .lvert = mr->lverts, + .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, + }, + extractors); } break; } } -static void extract_init(ExtractTaskData *data) +static void extract_task_init(ExtractTaskData *data) { - if (data->tasktype == EXTRACT_MESH_EXTRACT) { - data->user_data->user_data = data->extract->init(data->mr, data->cache, data->buf); - } + extract_init(data->mr, data->cache, data->extractors, data->mbc); } -static void extract_run(void *__restrict taskdata) +static void extract_task_run(void *__restrict taskdata) { ExtractTaskData *data = (ExtractTaskData *)taskdata; - if (data->tasktype == EXTRACT_MESH_EXTRACT) { - mesh_extract_iter(data->mr, - data->iter_type, - data->start, - data->end, - data->extract, - data->user_data->user_data); + mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, data->extractors); - /* If this is the last task, we do the finish function. */ - int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); - if (remainin_tasks == 0 && data->extract->finish != NULL) { - data->extract->finish(data->mr, data->cache, data->buf, data->user_data->user_data); - } - } - else if (data->tasktype == EXTRACT_LINES_LOOSE) { - extract_lines_loose_subbuffer(data->mr, data->cache); + /* If this is the last task, we do the finish function. */ + int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); + if (remainin_tasks == 0) { + extract_finish(data->mr, data->cache, data->extractors); } } -static void extract_init_and_run(void *__restrict taskdata) +static void extract_task_init_and_run(void *__restrict taskdata) { - extract_init((ExtractTaskData *)taskdata); - extract_run(taskdata); + ExtractTaskData *data = (ExtractTaskData *)taskdata; + extract_run_and_finish_init(data->mr, data->cache, data->extractors, data->iter_type, data->mbc); } /** \} */ @@ -5740,36 +6038,15 @@ static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *t /* ---------------------------------------------------------------------- */ /** \name Task Node - Extract Single Threaded * \{ */ -typedef struct ExtractSingleThreadedTaskData { - ListBase task_datas; -} ExtractSingleThreadedTaskData; - -static void extract_single_threaded_task_data_free(ExtractSingleThreadedTaskData *taskdata) -{ - BLI_assert(taskdata); - LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) { - extract_task_data_free(td); - } - BLI_listbase_clear(&taskdata->task_datas); - MEM_freeN(taskdata); -} - -static void extract_single_threaded_task_node_exec(void *__restrict task_data) -{ - ExtractSingleThreadedTaskData *extract_task_data = task_data; - LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) { - extract_init_and_run(td); - } -} -static struct TaskNode *extract_single_threaded_task_node_create( - struct TaskGraph *task_graph, ExtractSingleThreadedTaskData *task_data) +static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph, + ExtractTaskData *task_data) { struct TaskNode *task_node = BLI_task_graph_node_create( task_graph, - extract_single_threaded_task_node_exec, + extract_task_init_and_run, task_data, - (TaskGraphNodeFreeFunction)extract_single_threaded_task_data_free); + (TaskGraphNodeFreeFunction)extract_task_data_free); return task_node; } @@ -5779,28 +6056,23 @@ static struct TaskNode *extract_single_threaded_task_node_create( /** \name Task Node - UserData Initializer * \{ */ typedef struct UserDataInitTaskData { - ListBase task_datas; - int32_t *task_counters; + ExtractTaskData *td; + int32_t task_counter; } UserDataInitTaskData; static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) { BLI_assert(taskdata); - LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) { - extract_task_data_free(td); - } - BLI_listbase_clear(&taskdata->task_datas); - MEM_SAFE_FREE(taskdata->task_counters); + extract_task_data_free(taskdata->td); MEM_freeN(taskdata); } static void user_data_init_task_data_exec(void *__restrict task_data) { UserDataInitTaskData *extract_task_data = task_data; - LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) { - extract_init(td); - } + ExtractTaskData *taskdata_base = extract_task_data->td; + extract_task_init(taskdata_base); } static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, @@ -5815,6 +6087,53 @@ static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_g } /** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Override extractors + * Extractors can be overridden. When overridden a specialized version is used. The next functions + * would check for any needed overrides and usage of the specialized version. + * \{ */ + +static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor) +{ + if (extractor == &extract_pos_nor) { + return &extract_pos_nor_hq; + } + if (extractor == &extract_lnor) { + return &extract_lnor_hq; + } + if (extractor == &extract_tan) { + return &extract_tan_hq; + } + if (extractor == &extract_fdots_nor) { + return &extract_fdots_nor_hq; + } + return extractor; +} + +static const MeshExtract *mesh_extract_override_loose_lines(const MeshExtract *extractor) +{ + if (extractor == &extract_lines) { + return &extract_lines_with_lines_loose; + } + return extractor; +} + +static const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals, + const bool do_lines_loose_subbuffer) +{ + if (do_hq_normals) { + extractor = mesh_extract_override_hq_normals(extractor); + } + if (do_lines_loose_subbuffer) { + extractor = mesh_extract_override_loose_lines(extractor); + } + return extractor; +} + +/** \} */ + /* ---------------------------------------------------------------------- */ /** \name Extract Loop * \{ */ @@ -5832,93 +6151,79 @@ static void extract_range_task_create(struct TaskGraph *task_graph, taskdata->start = start; taskdata->end = start + length; struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, extract_run, taskdata, MEM_freeN); + task_graph, extract_task_run, taskdata, MEM_freeN); BLI_task_graph_edge_create(task_node_user_data_init, task_node); } -static void extract_task_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_mesh_render_data, - struct TaskNode *task_node_user_data_init, - ListBase *single_threaded_task_datas, - ListBase *user_data_init_task_datas, - const Scene *scene, - const MeshRenderData *mr, - MeshBatchCache *cache, - const MeshExtract *extract, - void *buf, - int32_t *task_counter) +static int extract_range_task_num_elements_get(const MeshRenderData *mr, + const eMRIterType iter_type) { - BLI_assert(scene != NULL); - const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || - GPU_use_hq_normals_workaround(); - if (do_hq_normals) { - if (extract == &extract_lnor) { - extract = &extract_lnor_hq; - } - else if (extract == &extract_pos_nor) { - extract = &extract_pos_nor_hq; - } - else if (extract == &extract_tan) { - extract = &extract_tan_hq; - } - else if (extract == &extract_fdots_nor) { - extract = &extract_fdots_nor_hq; - } + /* Divide task into sensible chunks. */ + int iter_len = 0; + if (iter_type & MR_ITER_LOOPTRI) { + iter_len += mr->tri_len; } + if (iter_type & MR_ITER_POLY) { + iter_len += mr->poly_len; + } + if (iter_type & MR_ITER_LEDGE) { + iter_len += mr->edge_loose_len; + } + if (iter_type & MR_ITER_LVERT) { + iter_len += mr->vert_loose_len; + } + return iter_len; +} - /* Divide extraction of the VBO/IBO into sensible chunks of works. */ - ExtractTaskData *taskdata = extract_task_data_create_mesh_extract( - mr, cache, extract, buf, task_counter); +static int extract_range_task_chunk_size_get(const MeshRenderData *mr, + const eMRIterType iter_type, + const int num_threads) +{ + /* Divide task into sensible chunks. */ + const int num_elements = extract_range_task_num_elements_get(mr, iter_type); + int range_len = (num_elements + num_threads) / num_threads; + CLAMP_MIN(range_len, CHUNK_SIZE); + return range_len; +} - /* Simple heuristic. */ - const int chunk_size = 8192; - const bool use_thread = (mr->loop_len + mr->loop_loose_len) > chunk_size; - if (use_thread && extract->use_threading) { - - /* Divide task into sensible chunks. */ - if (taskdata->iter_type & MR_ITER_LOOPTRI) { - for (int i = 0; i < mr->tri_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOPTRI, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_POLY) { - for (int i = 0; i < mr->poly_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_POLY, i, chunk_size); - } - } - if (taskdata->iter_type & MR_ITER_LEDGE) { - for (int i = 0; i < mr->edge_loose_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LEDGE, i, chunk_size); - } +static void extract_task_in_ranges_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata_base, + const int num_threads) +{ + const MeshRenderData *mr = taskdata_base->mr; + const int range_len = extract_range_task_chunk_size_get( + mr, taskdata_base->iter_type, num_threads); + + if (taskdata_base->iter_type & MR_ITER_LOOPTRI) { + for (int i = 0; i < mr->tri_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len); } - if (taskdata->iter_type & MR_ITER_LVERT) { - for (int i = 0; i < mr->vert_loose_len; i += chunk_size) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata, MR_ITER_LVERT, i, chunk_size); - } + } + if (taskdata_base->iter_type & MR_ITER_POLY) { + for (int i = 0; i < mr->poly_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len); } - BLI_addtail(user_data_init_task_datas, taskdata); } - else if (use_thread) { - /* One task for the whole VBO. */ - (*task_counter)++; - struct TaskNode *one_task = BLI_task_graph_node_create( - task_graph, extract_init_and_run, taskdata, extract_task_data_free); - BLI_task_graph_edge_create(task_node_mesh_render_data, one_task); + if (taskdata_base->iter_type & MR_ITER_LEDGE) { + for (int i = 0; i < mr->edge_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len); + } } - else { - /* Single threaded extraction. */ - (*task_counter)++; - BLI_addtail(single_threaded_task_datas, taskdata); + if (taskdata_base->iter_type & MR_ITER_LVERT) { + for (int i = 0; i < mr->vert_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len); + } } } void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshBatchCache *cache, - MeshBufferCache mbc, + MeshBufferCache *mbc, MeshBufferExtractionCache *extraction_cache, Mesh *me, @@ -5935,8 +6240,8 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool use_hide) { /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. - * This sub-graph starts with an extract_render_data_node. This fills/converts the required data - * from Mesh. + * This sub-graph starts with an extract_render_data_node. This fills/converts the required + * data from Mesh. * * Small extractions and extractions that can't be multi-threaded are grouped in a single * `extract_single_threaded_task_node`. @@ -5964,69 +6269,67 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, * +-----> | extract_task2_loop_3 | * +----------------------+ */ - eMRIterType iter_flag = 0; - eMRDataType data_flag = 0; + const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != NULL; + const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || + GPU_use_hq_normals_workaround(); - const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose != NULL; - bool do_extract = false; + /* Create an array containing all the extractors that needs to be executed. */ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_init(&extractors); -#define TEST_ASSIGN(type, type_lowercase, name) \ +#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ do { \ - if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \ - iter_flag |= mesh_extract_iter_type(&extract_##name); \ - data_flag |= extract_##name.data_flag; \ - do_extract = true; \ + if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ + const MeshExtract *extractor = mesh_extract_override_get( \ + &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ + mesh_extract_run_data_array_add(&extractors, extractor); \ } \ } while (0) - TEST_ASSIGN(VBO, vbo, pos_nor); - TEST_ASSIGN(VBO, vbo, lnor); - TEST_ASSIGN(VBO, vbo, uv); - TEST_ASSIGN(VBO, vbo, tan); - TEST_ASSIGN(VBO, vbo, vcol); - TEST_ASSIGN(VBO, vbo, sculpt_data); - TEST_ASSIGN(VBO, vbo, orco); - TEST_ASSIGN(VBO, vbo, edge_fac); - TEST_ASSIGN(VBO, vbo, weights); - TEST_ASSIGN(VBO, vbo, edit_data); - TEST_ASSIGN(VBO, vbo, edituv_data); - TEST_ASSIGN(VBO, vbo, edituv_stretch_area); - TEST_ASSIGN(VBO, vbo, edituv_stretch_angle); - TEST_ASSIGN(VBO, vbo, mesh_analysis); - TEST_ASSIGN(VBO, vbo, fdots_pos); - TEST_ASSIGN(VBO, vbo, fdots_nor); - TEST_ASSIGN(VBO, vbo, fdots_uv); - TEST_ASSIGN(VBO, vbo, fdots_edituv_data); - TEST_ASSIGN(VBO, vbo, poly_idx); - TEST_ASSIGN(VBO, vbo, edge_idx); - TEST_ASSIGN(VBO, vbo, vert_idx); - TEST_ASSIGN(VBO, vbo, fdot_idx); - TEST_ASSIGN(VBO, vbo, skin_roots); - - TEST_ASSIGN(IBO, ibo, tris); - TEST_ASSIGN(IBO, ibo, lines); - TEST_ASSIGN(IBO, ibo, points); - TEST_ASSIGN(IBO, ibo, fdots); - TEST_ASSIGN(IBO, ibo, lines_paint_mask); - TEST_ASSIGN(IBO, ibo, lines_adjacency); - TEST_ASSIGN(IBO, ibo, edituv_tris); - TEST_ASSIGN(IBO, ibo, edituv_lines); - TEST_ASSIGN(IBO, ibo, edituv_points); - TEST_ASSIGN(IBO, ibo, edituv_fdots); - - if (!do_extract) { + EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, lnor); + EXTRACT_ADD_REQUESTED(VBO, vbo, uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, tan); + EXTRACT_ADD_REQUESTED(VBO, vbo, vcol); + EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, orco); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac); + EXTRACT_ADD_REQUESTED(VBO, vbo, weights); + EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle); + EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots); + + EXTRACT_ADD_REQUESTED(IBO, ibo, tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, points); + EXTRACT_ADD_REQUESTED(IBO, ibo, fdots); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots); + +#undef EXTRACT_ADD_REQUESTED + + if (extractors.len == 0) { return; } - if (do_lines_loose_subbuffer) { - iter_flag |= MR_ITER_LEDGE; - } - -#undef TEST_ASSIGN - -#ifdef DEBUG_TIME - double rdata_start = PIL_check_seconds_timer(); -#endif + eMRIterType iter_type; + eMRDataType data_flag; + extracts_flags_get(&extractors, &iter_type, &data_flag); MeshRenderData *mr = mesh_render_data_create(me, extraction_cache, @@ -6038,124 +6341,91 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, do_uvedit, cd_layer_used, ts, - iter_flag); + iter_type); mr->use_hide = use_hide; mr->use_subsurf_fdots = use_subsurf_fdots; mr->use_final_mesh = do_final; #ifdef DEBUG_TIME - double rdata_end = PIL_check_seconds_timer(); + rdata_end = PIL_check_seconds_timer(); #endif - size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t); - int32_t *task_counters = MEM_callocN(counters_size, __func__); - int counter_used = 0; - struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( - task_graph, mr, iter_flag, data_flag); - ExtractSingleThreadedTaskData *single_threaded_task_data = MEM_callocN( - sizeof(ExtractSingleThreadedTaskData), __func__); - UserDataInitTaskData *user_data_init_task_data = MEM_callocN(sizeof(UserDataInitTaskData), - __func__); - user_data_init_task_data->task_counters = task_counters; - struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( - task_graph, user_data_init_task_data); - -#define EXTRACT(buf, name) \ - if (mbc.buf.name) { \ - extract_task_create(task_graph, \ - task_node_mesh_render_data, \ - task_node_user_data_init, \ - &single_threaded_task_data->task_datas, \ - &user_data_init_task_data->task_datas, \ - scene, \ - mr, \ - cache, \ - &extract_##name, \ - mbc.buf.name, \ - &task_counters[counter_used++]); \ - } \ - ((void)0) - - EXTRACT(vbo, pos_nor); - EXTRACT(vbo, lnor); - EXTRACT(vbo, uv); - EXTRACT(vbo, tan); - EXTRACT(vbo, vcol); - EXTRACT(vbo, sculpt_data); - EXTRACT(vbo, orco); - EXTRACT(vbo, edge_fac); - EXTRACT(vbo, weights); - EXTRACT(vbo, edit_data); - EXTRACT(vbo, edituv_data); - EXTRACT(vbo, edituv_stretch_area); - EXTRACT(vbo, edituv_stretch_angle); - EXTRACT(vbo, mesh_analysis); - EXTRACT(vbo, fdots_pos); - EXTRACT(vbo, fdots_nor); - EXTRACT(vbo, fdots_uv); - EXTRACT(vbo, fdots_edituv_data); - EXTRACT(vbo, poly_idx); - EXTRACT(vbo, edge_idx); - EXTRACT(vbo, vert_idx); - EXTRACT(vbo, fdot_idx); - EXTRACT(vbo, skin_roots); - - EXTRACT(ibo, tris); - if (mbc.ibo.lines) { - /* When `lines` and `lines_loose` are requested, schedule lines extraction that also creates - * the `lines_loose` sub-buffer. */ - const MeshExtract *lines_extractor = do_lines_loose_subbuffer ? - &extract_lines_with_lines_loose : - &extract_lines; - extract_task_create(task_graph, - task_node_mesh_render_data, - task_node_user_data_init, - &single_threaded_task_data->task_datas, - &user_data_init_task_data->task_datas, - scene, - mr, - cache, - lines_extractor, - mbc.ibo.lines, - &task_counters[counter_used++]); - } - else { - if (do_lines_loose_subbuffer) { - ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr, cache); - BLI_addtail(&single_threaded_task_data->task_datas, taskdata); - } - } - EXTRACT(ibo, points); - EXTRACT(ibo, fdots); - EXTRACT(ibo, lines_paint_mask); - EXTRACT(ibo, lines_adjacency); - EXTRACT(ibo, edituv_tris); - EXTRACT(ibo, edituv_lines); - EXTRACT(ibo, edituv_points); - EXTRACT(ibo, edituv_fdots); - - /* Only create the edge when there is user data that needs to be initialized. - * The task is still part of the graph so the task_data will be freed when the graph is freed. - */ - if (!BLI_listbase_is_empty(&user_data_init_task_data->task_datas)) { - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); - } + task_graph, mr, iter_type, data_flag); - if (!BLI_listbase_is_empty(&single_threaded_task_data->task_datas)) { - struct TaskNode *task_node = extract_single_threaded_task_node_create( - task_graph, single_threaded_task_data); - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + /* Simple heuristic. */ + const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; + + if (use_thread) { + uint threads_to_use = 0; + + /* First run the requested extractors that do not support asynchronous ranges. */ + for (int i = 0; i < extractors.len; i++) { + const MeshExtract *extractor = extractors.items[i].extractor; + if (!extractor->use_threading) { + MeshExtractRunDataArray *single_threaded_extractors = MEM_callocN( + sizeof(MeshExtractRunDataArray), + "mesh_buffer_cache_create_requested.single_threaded_extractors"); + mesh_extract_run_data_array_add(single_threaded_extractors, extractor); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, single_threaded_extractors, mbc, NULL); + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, + taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + threads_to_use++; + } + + /* Distribute the remaining extractors into ranges per core. */ + MeshExtractRunDataArray *multi_threaded_extractors = MEM_callocN( + sizeof(MeshExtractRunDataArray), + "mesh_buffer_cache_create_requested.multi_threaded_extractors"); + mesh_extract_run_data_array_filter_threading(&extractors, multi_threaded_extractors); + if (multi_threaded_extractors->len) { + /* + * Determine the number of thread to use for multithreading. + * Thread can be used for single threaded tasks. These typically take longer to execute so + * fill the rest of the threads for range operations. + */ + int num_threads = BLI_task_scheduler_num_threads(); + if (threads_to_use < num_threads) { + num_threads -= threads_to_use; + } + + UserDataInitTaskData *user_data_init_task_data = MEM_callocN( + sizeof(UserDataInitTaskData), + "mesh_buffer_cache_create_requested.user_data_init_task_data"); + struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( + task_graph, user_data_init_task_data); + + user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh( + mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter); + + extract_task_in_ranges_create( + task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads); + + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); + } + else { + /* No tasks created freeing extractors list. */ + MEM_freeN(multi_threaded_extractors); + } } else { - extract_single_threaded_task_data_free(single_threaded_task_data); + /* Run all requests on the same thread. */ + MeshExtractRunDataArray *extractors_copy = MEM_mallocN( + sizeof(MeshExtractRunDataArray), "mesh_buffer_cache_create_requested.extractors_copy"); + memcpy(extractors_copy, &extractors, sizeof(MeshExtractRunDataArray)); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, extractors_copy, mbc, NULL); + + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); } /* Trigger the sub-graph for this mesh. */ BLI_task_graph_node_push_work(task_node_mesh_render_data); -#undef EXTRACT - #ifdef DEBUG_TIME BLI_task_graph_work_and_wait(task_graph); double end = PIL_check_seconds_timer(); diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 5cf99db5485..ee6a47e3dc6 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -843,6 +843,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, int edges_len_capacity = curve_render_data_overlay_edges_len_get(rdata) * 2; int vbo_len_used = 0; +#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) +#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL)) + if (DRW_TEST_ASSIGN_VBO(vbo_pos)) { GPU_vertbuf_init_with_format(vbo_pos, &format_pos); GPU_vertbuf_data_alloc(vbo_pos, verts_len_capacity); @@ -863,6 +866,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata, GPU_indexbuf_init(elbp_lines, GPU_PRIM_LINES, edges_len_capacity, verts_len_capacity); } +#undef DRW_TEST_ASSIGN_VBO +#undef DRW_TEST_ASSIGN_IBO + int nu_id = 0; for (Nurb *nu = (Nurb *)rdata->nurbs->first; nu; nu = nu->next, nu_id++) { const BezTriple *bezt = nu->bezt; diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c index d606f70db9e..ee16cb1a022 100644 --- a/source/blender/draw/intern/draw_cache_impl_displist.c +++ b/source/blender/draw/intern/draw_cache_impl_displist.c @@ -532,6 +532,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb, GPUVertBufRaw uv_step = {0}; GPUVertBufRaw tan_step = {0}; +#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) + if (DRW_TEST_ASSIGN_VBO(vbo_pos_nor)) { GPU_vertbuf_init_with_format(vbo_pos_nor, do_hq_normals ? &format_pos_nor_hq : &format_pos_nor); @@ -550,6 +552,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb, GPU_vertbuf_attr_get_raw_data(vbo_tan, tan_id, &tan_step); } +#undef DRW_TEST_ASSIGN_VBO + BKE_displist_normals_add(lb); LISTBASE_FOREACH (const DispList *, dl, lb) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index c2dc9b3ad8d..0d91432d4e5 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -1559,7 +1559,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (do_uvcage) { mesh_buffer_cache_create_requested(task_graph, cache, - cache->uv_cage, + &cache->uv_cage, &cache->uv_cage_extraction_cache, me, is_editmode, @@ -1578,7 +1578,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (do_cage) { mesh_buffer_cache_create_requested(task_graph, cache, - cache->cage, + &cache->cage, &cache->cage_extraction_cache, me, is_editmode, @@ -1596,7 +1596,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, mesh_buffer_cache_create_requested(task_graph, cache, - cache->final, + &cache->final, &cache->final_extraction_cache, me, is_editmode, diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h index bfc714e5d6a..6e537a3bffa 100644 --- a/source/blender/draw/intern/draw_cache_inline.h +++ b/source/blender/draw/intern/draw_cache_inline.h @@ -40,10 +40,6 @@ (flag |= DRW_ibo_requested(ibo) ? (value) : 0) #endif -/* Test and assign NULL if test fails */ -#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL)) -#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL)) - BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch) { /* XXX TODO(fclem): We are writing to batch cache here. Need to make this thread safe. */ -- cgit v1.2.3 From b862916eafc809cc1d93ffc8cdfc48402df30df0 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 31 May 2021 17:27:02 +0200 Subject: VSE: Fix missing cache invalidation Fixes T88606 --- source/blender/makesrna/intern/rna_color.c | 2 +- source/blender/makesrna/intern/rna_space.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 206ebc2cb14..54f9a93d90a 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -650,7 +650,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, seq->strip->proxy->anim = NULL; } - SEQ_relations_invalidate_cache_preprocessed(scene, seq); + SEQ_relations_invalidate_cache_raw(scene, seq); } else { SEQ_ALL_BEGIN (scene->ed, seq) { diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index d744f67c6f6..0af2572a4bd 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5466,7 +5466,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXIES); RNA_def_property_ui_text( prop, "Use Proxies", "Use optimized files for faster scrubbing when available"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache"); /* grease pencil */ prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); -- cgit v1.2.3 From 46a14bd6a317ead29dfdbf038bd30d49a656249e Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 31 May 2021 17:24:35 +0200 Subject: Fix T88670: Load Previous Settings does not copy symlinks The same code existed in 2.82 and earlier so this should be safe. Removing the custom implementation of shutil.copytree in f34d5d9 did not correctly add back the option to copy symlinks. --- release/scripts/startup/bl_operators/userpref.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 7547184dc04..564a7b69957 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -144,7 +144,7 @@ class PREFERENCES_OT_copy_prev(Operator): def execute(self, _context): import shutil - shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True) + shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True, symlinks=True) # reload preferences and recent-files.txt bpy.ops.wm.read_userpref() -- cgit v1.2.3 From d647e730fbc23233b572594eeb6083978bdb882d Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Mon, 31 May 2021 09:59:29 -0600 Subject: Win: Fix warnings as errors being off for bmesh bf_bmesh historically always build with the /WX flag on windows making all warnings errors, somewhere along the way this has broken for msbuild, ninja still exhibits the expected behaviour. The flags are still passed to the target, and I've validated they are there when the add_library call fires, but they somehow never make it to the generated msbuild project files. I suspect this is a cmake bug but I'm seemingly unable to extract a repro case to file a bug upstream. Setting the same options target_compile_options seems to work, I'm not happy about the unexplained nature of the breakage but this will have to do for now. --- source/blender/bmesh/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index c215cf69e3a..92064b3d040 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -182,10 +182,6 @@ set(LIB extern_rangetree ) -if(MSVC AND NOT MSVC_CLANG) - string(APPEND CMAKE_C_FLAGS " /WX /wd4101") -endif() - if(WITH_BULLET) list(APPEND INC_SYS ${BULLET_INCLUDE_DIRS} @@ -225,6 +221,10 @@ endif() blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") +if(MSVC AND NOT MSVC_CLANG) + target_compile_options(bf_bmesh PRIVATE /WX /wd4101) +endif() + if(WITH_GTESTS) set(TEST_SRC tests/bmesh_core_test.cc -- cgit v1.2.3 From 261a10edb0a9da53b2554ca64bcf445a8b9b3d9f Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 31 May 2021 18:12:44 +0200 Subject: Display source video fps in the VSE Now FPS is displayed in the video source for videos to provide easy access. Reviewed By: Richard Antalik Differential Revision: http://developer.blender.org/D11441 --- release/scripts/startup/bl_ui/space_sequencer.py | 12 ++++++++++-- source/blender/makesdna/DNA_sequence_types.h | 1 + source/blender/makesrna/intern/rna_sequencer.c | 5 +++++ source/blender/sequencer/intern/render.c | 6 ++++++ source/blender/sequencer/intern/strip_add.c | 13 ++++++++++++- 5 files changed, 34 insertions(+), 3 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index b24b6e84939..e9bfe6cd4e2 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1398,8 +1398,8 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): box.template_image_stereo_3d(strip.stereo_3d_format) # Resolution. - col = layout.column(align=True) - col = col.box() + col = layout.box() + col = col.column(align=True) split = col.split(factor=0.5, align=False) split.alignment = 'RIGHT' split.label(text="Resolution") @@ -1409,6 +1409,14 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): split.label(text="%dx%d" % size, translate=False) else: split.label(text="None") + #FPS + if elem.orig_fps: + split = col.split(factor=0.5, align=False) + split.alignment = 'RIGHT' + split.label(text="FPS") + split.alignment = 'LEFT' + split.label(text="%.2f" % elem.orig_fps, translate=False) + class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel): diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 4b95dd41b30..f59f51ea28a 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -57,6 +57,7 @@ typedef struct StripElem { char name[256]; /** Ignore when zeroed. */ int orig_width, orig_height; + float orig_fps; } StripElem; typedef struct StripCrop { diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 9ba92431723..8fbad449cf6 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1346,6 +1346,11 @@ static void rna_def_strip_element(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "orig_height"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Orig Height", "Original image height"); + + prop = RNA_def_property(srna, "orig_fps", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "orig_fps"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second"); } static void rna_def_strip_crop(BlenderRNA *brna) diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index d881c90a1e0..8ed769880a4 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1237,6 +1237,12 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, } if (*r_is_proxy_image == false) { + if (sanim && sanim->anim) { + short fps_denom; + float fps_num; + IMB_anim_get_fps(sanim->anim, &fps_denom, &fps_num, true); + seq->strip->stripdata->orig_fps = fps_denom / fps_num; + } seq->strip->stripdata->orig_width = ibuf->x; seq->strip->stripdata->orig_height = ibuf->y; } diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 5ec2269b993..64671aeb265 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -548,15 +548,25 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ + float video_fps = 0.0f; + if (anim_arr[0] != NULL) { seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); IMB_anim_load_metadata(anim_arr[0]); + short fps_denom; + float fps_num; + + IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); + + video_fps = fps_denom / fps_num; + /* Adjust scene's frame rate settings to match. */ if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { - IMB_anim_get_fps(anim_arr[0], &scene->r.frs_sec, &scene->r.frs_sec_base, true); + scene->r.frs_sec = fps_denom; + scene->r.frs_sec_base = fps_num; } /* Set initial scale based on load_data->fit_method. */ @@ -577,6 +587,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem"); strip->stripdata->orig_width = orig_width; strip->stripdata->orig_height = orig_height; + strip->stripdata->orig_fps = video_fps; BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name)); seq_add_set_view_transform(scene, seq, load_data); -- cgit v1.2.3 From ee54a8ace753a56236f5d1d647da7a40904af6b3 Mon Sep 17 00:00:00 2001 From: James Monteath Date: Mon, 31 May 2021 18:58:42 +0200 Subject: Update all README to clearify intention or usage Add snap configuration file used by Buildbot snap store steps1 --- build_files/buildbot/README.md | 7 ++-- build_files/config/README.md | 4 +- release/darwin/README.md | 8 ++-- release/freedesktop/snap/README.md | 20 +++++++-- .../snap/blender-snapcraft-template.yaml | 49 ++++++++++++++++++++++ release/windows/msix/README.md | 7 ++-- 6 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 release/freedesktop/snap/blender-snapcraft-template.yaml diff --git a/build_files/buildbot/README.md b/build_files/buildbot/README.md index 9c71deeec68..f6fd07d9246 100644 --- a/build_files/buildbot/README.md +++ b/build_files/buildbot/README.md @@ -1,5 +1,4 @@ -Moved Scripts -============= +Buildbot Configuration +===================== -Scripts have been moved to own git repo -Only configurations remain here and is used with new pipeline +Files used by Buildbot's `compile-code` step. diff --git a/build_files/config/README.md b/build_files/config/README.md index 958a01d9aa2..6c429e4e58d 100644 --- a/build_files/config/README.md +++ b/build_files/config/README.md @@ -1,10 +1,8 @@ Pipeline Config =============== -Scripts have been moved to own git repo. - This configuration file is used by buildbot new pipeline for the `update-code` step. It will soon be used by the ../utils/make_update.py script. -Both buildbot and developers will eventually use the same configuration file. \ No newline at end of file +Both buildbot and developers will eventually use the same configuration file. diff --git a/release/darwin/README.md b/release/darwin/README.md index 9c71deeec68..f1f02543ff3 100644 --- a/release/darwin/README.md +++ b/release/darwin/README.md @@ -1,5 +1,5 @@ -Moved Scripts -============= +Buildbot Configuration +====================== + +Files used by Buildbot's `package-code-binaires` step for the darwin platform. -Scripts have been moved to own git repo -Only configurations remain here and is used with new pipeline diff --git a/release/freedesktop/snap/README.md b/release/freedesktop/snap/README.md index 9c71deeec68..742b265ada6 100644 --- a/release/freedesktop/snap/README.md +++ b/release/freedesktop/snap/README.md @@ -1,5 +1,17 @@ -Moved Scripts -============= +Snap Configuration +=================== -Scripts have been moved to own git repo -Only configurations remain here and is used with new pipeline +Files used by Buildbot's `package-code-store-snap` and `deliver-code-store-snap` steps. + +Build pipeline snap tracks and channels + +``` + /stable + - Latest stable release for the specified track + /candidate + - Test builds for the upcoming stable release - *not used for now* + /beta + - Nightly automated builds provided by a release branch + /egde/ + - Nightly or on demand builds - will also make use of branch +``` diff --git a/release/freedesktop/snap/blender-snapcraft-template.yaml b/release/freedesktop/snap/blender-snapcraft-template.yaml new file mode 100644 index 00000000000..882f9081c09 --- /dev/null +++ b/release/freedesktop/snap/blender-snapcraft-template.yaml @@ -0,0 +1,49 @@ +name: blender +summary: Blender is the free and open source 3D creation suite. +description: | + Blender is the free and open source 3D creation suite. It supports the + entirety of the 3D pipeline—modeling, rigging, animation, simulation, + rendering, compositing and motion tracking, and video editing. + + Blender is a public project, made by hundreds of people from around the + world; by studios and individual artists, professionals and hobbyists, + scientists, students, VFX experts, animators, game artists, modders, and + the list goes on. + +icon: @ICON_PATH@ + +passthrough: + license: GPL-3.0 + +confinement: classic + +apps: + blender: + command: ./blender-wrapper + desktop: ./blender.desktop + +base: core18 +version: '@VERSION@' +grade: @GRADE@ + +parts: + blender: + plugin: dump + source: @PACKAGE_PATH@ + build-attributes: [keep-execstack, no-patchelf] + override-build: | + snapcraftctl build + sed -i 's|Icon=blender|Icon=${SNAP}/blender.svg|' ${SNAPCRAFT_PART_INSTALL}/blender.desktop + stage-packages: + - libxcb1 + - libxext6 + - libx11-6 + - libxi6 + - libxfixes3 + - libxrender1 + - libxxf86vm1 + wrapper: + plugin: dump + source: . + stage: + - ./blender-wrapper diff --git a/release/windows/msix/README.md b/release/windows/msix/README.md index 9c71deeec68..96f753f0e78 100644 --- a/release/windows/msix/README.md +++ b/release/windows/msix/README.md @@ -1,5 +1,4 @@ -Moved Scripts -============= +Buildbot Configuration +====================== -Scripts have been moved to own git repo -Only configurations remain here and is used with new pipeline +Files used by Buildbot's `package-code-store-windows` step. -- cgit v1.2.3 From 875a8a6c79b0c5da1715b70eaa58b12f9b816767 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Mon, 31 May 2021 10:06:38 -0700 Subject: Cleanup: Replace fseek() calls with BLI_fseek() The fseek() function on Windows only accepts a 32-bit long offset argument. Because of this we have our own version, BLI_fseek(), which will use 64-bit _fseeki64() on Windows. This patch just replaces some fseek() calls with BLI_fseek(). Differential Revision: https://developer.blender.org/D11430 Reviewed by Brecht Van Lommel --- source/blender/blenfont/intern/blf_font_win32_compat.c | 6 +++--- source/blender/blenkernel/intern/customdata_file.c | 8 ++++---- source/blender/blenlib/intern/fileops.c | 4 ++-- source/blender/sequencer/intern/image_cache.c | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/blender/blenfont/intern/blf_font_win32_compat.c b/source/blender/blenfont/intern/blf_font_win32_compat.c index 7d130204c07..e573d3bd224 100644 --- a/source/blender/blenfont/intern/blf_font_win32_compat.c +++ b/source/blender/blenfont/intern/blf_font_win32_compat.c @@ -66,7 +66,7 @@ static unsigned long ft_ansi_stream_io(FT_Stream stream, file = STREAM_FILE(stream); if (stream->pos != offset) { - fseek(file, offset, SEEK_SET); + BLI_fseek(file, offset, SEEK_SET); } return fread(buffer, 1, count, file); @@ -93,7 +93,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep return FT_THROW(Cannot_Open_Resource); } - fseek(file, 0, SEEK_END); + BLI_fseek(file, 0LL, SEEK_END); stream->size = ftell(file); if (!stream->size) { fprintf(stderr, @@ -104,7 +104,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep return FT_THROW(Cannot_Open_Stream); } - fseek(file, 0, SEEK_SET); + BLI_fseek(file, 0LL, SEEK_SET); stream->descriptor.pointer = file; stream->read = ft_ansi_stream_io; diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c index 470c2f2d246..4aaecc26eff 100644 --- a/source/blender/blenkernel/intern/customdata_file.c +++ b/source/blender/blenkernel/intern/customdata_file.c @@ -167,7 +167,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += header->structbytes; header->structbytes = sizeof(CDataFileHeader); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -201,7 +201,7 @@ static bool cdf_read_header(CDataFile *cdf) mesh->structbytes = sizeof(CDataFileMeshHeader); } - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -233,7 +233,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += layer->structbytes; layer->structbytes = sizeof(CDataFileLayer); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } } @@ -321,7 +321,7 @@ bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay) offset += cdf->layer[a].datasize; } - return (fseek(cdf->readf, offset, SEEK_SET) == 0); + return (BLI_fseek(cdf->readf, offset, SEEK_SET) == 0); } bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data) diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 106bd5bc793..107c27da6a2 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -171,7 +171,7 @@ size_t BLI_gzip_mem_to_file_at_pos( z_stream strm; unsigned char out[CHUNK]; - fseek(file, gz_stream_offset, 0); + BLI_fseek(file, gz_stream_offset, 0); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; @@ -217,7 +217,7 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g size_t chunk = 256 * 1024; unsigned char in[CHUNK]; - fseek(file, gz_stream_offset, 0); + BLI_fseek(file, gz_stream_offset, 0); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index a0c95c1c197..5ccf2a027b0 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -518,7 +518,7 @@ static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntr static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) { - fseek(file, 0, 0); + BLI_fseek(file, 0LL, SEEK_SET); const size_t num_items_read = fread(header, sizeof(*header), 1, file); if (num_items_read < 1) { BLI_assert(!"unable to read disk cache header"); @@ -540,7 +540,7 @@ static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header) { - fseek(file, 0, 0); + BLI_fseek(file, 0LL, SEEK_SET); return fwrite(header, sizeof(*header), 1, file); } -- cgit v1.2.3 From 25316ef9d735281a83b41f9e14fa3c712b1ada82 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 31 May 2021 16:21:24 +0200 Subject: Cycles: optimize 3D viewport rendering with camera passepartout If the area outside the camera is fully opaque, don't render it. Contributed by Kdaf. Differential Revision: https://developer.blender.org/D11182 --- intern/cycles/blender/blender_camera.cpp | 27 ++++++++++++++++++--------- intern/cycles/blender/blender_session.cpp | 13 ++++++------- intern/cycles/blender/blender_sync.h | 3 +-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index b31841801d8..6954c5c2f26 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -83,6 +83,8 @@ struct BlenderCamera { BoundBox2D pano_viewplane; BoundBox2D viewport_camera_border; + float passepartout_alpha; + Transform matrix; float offscreen_dicing_scale; @@ -125,6 +127,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->pano_viewplane.top = 1.0f; bcam->viewport_camera_border.right = 1.0f; bcam->viewport_camera_border.top = 1.0f; + bcam->passepartout_alpha = 0.5f; bcam->offscreen_dicing_scale = 1.0f; bcam->matrix = transform_identity(); @@ -212,6 +215,8 @@ static void blender_camera_from_object(BlenderCamera *bcam, bcam->lens = b_camera.lens(); + bcam->passepartout_alpha = b_camera.show_passepartout() ? b_camera.passepartout_alpha() : 0.0f; + if (b_camera.dof().use_dof()) { /* allow f/stop number to change aperture_size but still * give manual control over aperture radius */ @@ -834,15 +839,19 @@ static void blender_camera_border(BlenderCamera *bcam, full_border, &bcam->viewport_camera_border); - if (!b_render.use_border()) { + if (b_render.use_border()) { + bcam->border.left = b_render.border_min_x(); + bcam->border.right = b_render.border_max_x(); + bcam->border.bottom = b_render.border_min_y(); + bcam->border.top = b_render.border_max_y(); + } + else if (bcam->passepartout_alpha == 1.0f) { + bcam->border = full_border; + } + else { return; } - bcam->border.left = b_render.border_min_x(); - bcam->border.right = b_render.border_max_x(); - bcam->border.bottom = b_render.border_min_y(); - bcam->border.top = b_render.border_max_y(); - /* Determine viewport subset matching camera border. */ blender_camera_border_subset(b_engine, b_render, @@ -885,8 +894,7 @@ void BlenderSync::sync_view(BL::SpaceView3D &b_v3d, } } -BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, +BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, @@ -902,7 +910,8 @@ BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render, if (b_v3d && b_rv3d && b_rv3d.view_perspective() != BL::RegionView3D::view_perspective_CAMERA) use_border = b_v3d.use_render_border(); else - use_border = b_render.use_border(); + /* the camera can always have a passepartout */ + use_border = true; if (use_border) { /* border render */ diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 89854f6a0e5..29de886e4ff 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -155,7 +155,7 @@ void BlenderSession::create_session() /* set buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); session->reset(buffer_params, session_params.samples); b_engine.use_highlight_tiles(session_params.progressive_refine == false); @@ -242,8 +242,7 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL); BL::RegionView3D b_null_region_view3d(PointerRNA_NULL); - BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, - b_null_space_view3d, + BufferParams buffer_params = BlenderSync::get_buffer_params(b_null_space_view3d, b_null_region_view3d, scene->camera, width, @@ -486,7 +485,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background, b_view_layer); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); /* temporary render result to find needed passes and views */ BL::RenderResult b_rr = begin_render_result( @@ -810,7 +809,7 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) /* get buffer parameters */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); if (!buffer_params.denoising_data_pass) { session_params.denoising.use = false; @@ -889,7 +888,7 @@ bool BlenderSession::draw(int w, int h) SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session_pause == false) { @@ -907,7 +906,7 @@ bool BlenderSession::draw(int w, int h) /* draw */ BufferParams buffer_params = BlenderSync::get_buffer_params( - b_render, b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); DeviceDrawParams draw_params; if (session->params.display_buffer_linear) { diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 8cd65f13f70..1c98e529190 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -104,8 +104,7 @@ class BlenderSync { bool background, BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL)); static bool get_session_pause(BL::Scene &b_scene, bool background); - static BufferParams get_buffer_params(BL::RenderSettings &b_render, - BL::SpaceView3D &b_v3d, + static BufferParams get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, -- cgit v1.2.3 From 73967e2047b7d46168355a7158cd1c40d20afc4e Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 31 May 2021 16:28:00 -0300 Subject: Fix undeclared identifiers with 'DEBUG_TIME' These identifiers were accidentally removed in rB44d2479dc36f. --- source/blender/draw/intern/draw_cache_extract_mesh.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 1a78ce71d74..a2ec3e4e94e 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -6327,6 +6327,10 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, return; } +#ifdef DEBUG_TIME + double rdata_start = PIL_check_seconds_timer(); +#endif + eMRIterType iter_type; eMRDataType data_flag; extracts_flags_get(&extractors, &iter_type, &data_flag); @@ -6347,7 +6351,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, mr->use_final_mesh = do_final; #ifdef DEBUG_TIME - rdata_end = PIL_check_seconds_timer(); + double rdata_end = PIL_check_seconds_timer(); #endif struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( -- cgit v1.2.3 From 8180d478e1aefbbe538bd54b42dda388b482abf5 Mon Sep 17 00:00:00 2001 From: Erick Abrahammson Date: Mon, 31 May 2021 17:03:48 -0400 Subject: Speedup exact boolean by avoiding some mallocs and frees. This is from patch D11432 from Erik Abrahamsson. He found that in some mpq3 functions called frequently from loops, passing in buffers for termporary mpq3 values can save substantial time. On my machine, his example in that patch went from 9.48s to 7.50s for the boolean part of the calculation. On his machine, a running time went from 17s to 10.3s. --- source/blender/blenlib/BLI_mpq3.hh | 16 +++++ source/blender/blenlib/intern/mesh_boolean.cc | 94 +++++++++++++++++-------- source/blender/blenlib/intern/mesh_intersect.cc | 91 +++++++++++++++++------- 3 files changed, 146 insertions(+), 55 deletions(-) diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh index fb5e2b61cdb..b9eda2ad7e1 100644 --- a/source/blender/blenlib/BLI_mpq3.hh +++ b/source/blender/blenlib/BLI_mpq3.hh @@ -218,6 +218,15 @@ struct mpq3 { return a.x * b.x + a.y * b.y + a.z * b.z; } + static mpq_class dot_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer) + { + buffer = a; + buffer *= b; + buffer.x += buffer.y; + buffer.x += buffer.z; + return buffer.x; + } + static mpq3 cross(const mpq3 &a, const mpq3 &b) { return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); @@ -246,6 +255,13 @@ struct mpq3 { return mpq3::dot(diff, diff); } + static mpq_class distance_squared_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer) + { + buffer = a; + buffer -= b; + return mpq3::dot(buffer, buffer); + } + static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t) { mpq_class s = 1 - t; diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 431720761bc..cc27e9238c3 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -1695,9 +1695,24 @@ static int find_containing_cell(const Vert *v, * If the closest point is on an edge, return 0, 1, or 2 * for edges ab, bc, or ca in *r_edge; else -1. * (Adapted from #closest_on_tri_to_point_v3()). + * The arguments ab, ac, ..., r are used as temporaries + * in this routine. Passing them in from the caller can + * avoid many allocs and frees of temporary mpq3 values + * and the mpq_class values within them. */ -static mpq_class closest_on_tri_to_point( - const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert) +static mpq_class closest_on_tri_to_point(const mpq3 &p, + const mpq3 &a, + const mpq3 &b, + const mpq3 &c, + mpq3 &ab, + mpq3 &ac, + mpq3 &ap, + mpq3 &bp, + mpq3 &cp, + mpq3 &m, + mpq3 &r, + int *r_edge, + int *r_vert) { constexpr int dbg_level = 0; if (dbg_level > 0) { @@ -1705,11 +1720,15 @@ static mpq_class closest_on_tri_to_point( std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n"; } /* Check if p in vertex region outside a. */ - mpq3 ab = b - a; - mpq3 ac = c - a; - mpq3 ap = p - a; - mpq_class d1 = mpq3::dot(ab, ap); - mpq_class d2 = mpq3::dot(ac, ap); + ab = b; + ab -= a; + ac = c; + ac -= a; + ap = p; + ap -= a; + + mpq_class d1 = mpq3::dot_with_buffer(ab, ap, m); + mpq_class d2 = mpq3::dot_with_buffer(ac, ap, m); if (d1 <= 0 && d2 <= 0) { /* Barycentric coordinates (1,0,0). */ *r_edge = -1; @@ -1717,12 +1736,13 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = a\n"; } - return mpq3::distance_squared(p, a); + return mpq3::distance_squared_with_buffer(p, a, m); } /* Check if p in vertex region outside b. */ - mpq3 bp = p - b; - mpq_class d3 = mpq3::dot(ab, bp); - mpq_class d4 = mpq3::dot(ac, bp); + bp = p; + bp -= b; + mpq_class d3 = mpq3::dot_with_buffer(ab, bp, m); + mpq_class d4 = mpq3::dot_with_buffer(ac, bp, m); if (d3 >= 0 && d4 <= d3) { /* Barycentric coordinates (0,1,0). */ *r_edge = -1; @@ -1730,25 +1750,28 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = b\n"; } - return mpq3::distance_squared(p, b); + return mpq3::distance_squared_with_buffer(p, b, m); } /* Check if p in region of ab. */ mpq_class vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { mpq_class v = d1 / (d1 - d3); /* Barycentric coordinates (1-v,v,0). */ - mpq3 r = a + v * ab; + r = ab; + r *= v; + r += a; *r_vert = -1; *r_edge = 0; if (dbg_level > 0) { std::cout << " answer = on ab at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* Check if p in vertex region outside c. */ - mpq3 cp = p - c; - mpq_class d5 = mpq3::dot(ab, cp); - mpq_class d6 = mpq3::dot(ac, cp); + cp = p; + cp -= c; + mpq_class d5 = mpq3::dot_with_buffer(ab, cp, m); + mpq_class d6 = mpq3::dot_with_buffer(ac, cp, m); if (d6 >= 0 && d5 <= d6) { /* Barycentric coordinates (0,0,1). */ *r_edge = -1; @@ -1756,49 +1779,54 @@ static mpq_class closest_on_tri_to_point( if (dbg_level > 0) { std::cout << " answer = c\n"; } - return mpq3::distance_squared(p, c); + return mpq3::distance_squared_with_buffer(p, c, m); } /* Check if p in edge region of ac. */ mpq_class vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { mpq_class w = d2 / (d2 - d6); /* Barycentric coordinates (1-w,0,w). */ - mpq3 r = a + w * ac; + r = ac; + r *= w; + r += a; *r_vert = -1; *r_edge = 2; if (dbg_level > 0) { std::cout << " answer = on ac at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* Check if p in edge region of bc. */ mpq_class va = d3 * d6 - d5 * d4; if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) { mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); /* Barycentric coordinates (0,1-w,w). */ - mpq3 r = c - b; - r = w * r; - r = r + b; + r = c; + r -= b; + r *= w; + r += b; *r_vert = -1; *r_edge = 1; if (dbg_level > 0) { std::cout << " answer = on bc at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } /* p inside face region. Compute barycentric coordinates (u,v,w). */ mpq_class denom = 1 / (va + vb + vc); mpq_class v = vb * denom; mpq_class w = vc * denom; - ac = w * ac; - mpq3 r = a + v * ab; - r = r + ac; + ac *= w; + r = ab; + r *= v; + r += a; + r += ac; *r_vert = -1; *r_edge = -1; if (dbg_level > 0) { std::cout << " answer = inside at " << r << "\n"; } - return mpq3::distance_squared(p, r); + return mpq3::distance_squared_with_buffer(p, r, m); } struct ComponentContainer { @@ -1837,6 +1865,9 @@ static Vector find_component_containers(int comp, if (dbg_level > 0) { std::cout << "test vertex in comp: " << test_v << "\n"; } + + mpq3 buf[7]; + for (int comp_other : components.index_range()) { if (comp == comp_other) { continue; @@ -1861,6 +1892,13 @@ static Vector find_component_containers(int comp, tri[0]->co_exact, tri[1]->co_exact, tri[2]->co_exact, + buf[0], + buf[1], + buf[2], + buf[3], + buf[4], + buf[5], + buf[6], &close_edge, &close_vert); if (dbg_level > 1) { diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 636209883c3..97f856476c5 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -1165,13 +1165,19 @@ static int filter_plane_side(const double3 &p, * Assumes ab is not perpendicular to n. * This works because the ratio of the projections of ab and ac onto n is the same as * the ratio along the line ab of the intersection point to the whole of ab. + * The ab, ac, and dotbuf arguments are used as a temporaries; declaring them + * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures. */ -static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n) -{ - mpq3 ab = a - b; - mpq_class den = mpq3::dot(ab, n); +static inline mpq3 tti_interp( + const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n, mpq3 &ab, mpq3 &ac, mpq3 &dotbuf) +{ + ab = a; + ab -= b; + ac = a; + ac -= c; + mpq_class den = mpq3::dot_with_buffer(ab, n, dotbuf); BLI_assert(den != 0); - mpq_class alpha = mpq3::dot(a - c, n) / den; + mpq_class alpha = mpq3::dot_with_buffer(ac, n, dotbuf) / den; return a - alpha * ab; } @@ -1179,11 +1185,28 @@ static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const * Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW * order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations. * TODO: change arguments to `const Vert *` and use floating filters. + * The ba, ca, n, and dotbuf arguments are used as temporaries; declaring them + * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures. */ -static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad) +static inline int tti_above(const mpq3 &a, + const mpq3 &b, + const mpq3 &c, + const mpq3 &ad, + mpq3 &ba, + mpq3 &ca, + mpq3 &n, + mpq3 &dotbuf) { - mpq3 n = mpq3::cross(b - a, c - a); - return sgn(mpq3::dot(ad, n)); + ba = b; + ba -= a; + ca = c; + ca -= a; + + n.x = ba.y * ca.z - ba.z * ca.y; + n.y = ba.z * ca.x - ba.x * ca.z; + n.z = ba.x * ca.y - ba.y * ca.x; + + return sgn(mpq3::dot_with_buffer(ad, n, dotbuf)); } /** @@ -1227,20 +1250,21 @@ static ITT_value itt_canon2(const mpq3 &p1, mpq3 p1p2 = p2 - p1; mpq3 intersect_1; mpq3 intersect_2; + mpq3 buf[4]; bool no_overlap = false; /* Top test in classification tree. */ - if (tti_above(p1, q1, r2, p1p2) > 0) { + if (tti_above(p1, q1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) { /* Middle right test in classification tree. */ - if (tti_above(p1, r1, r2, p1p2) <= 0) { + if (tti_above(p1, r1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) <= 0) { /* Bottom right test in classification tree. */ - if (tti_above(p1, r1, q2, p1p2) > 0) { + if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) { /* Overlap is [k [i l] j]. */ if (dbg_level > 0) { std::cout << "overlap [k [i l] j]\n"; } /* i is intersect with p1r1. l is intersect with p2r2. */ - intersect_1 = tti_interp(p1, r1, p2, n2); - intersect_2 = tti_interp(p2, r2, p1, n1); + intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]); } else { /* Overlap is [i [k l] j]. */ @@ -1248,8 +1272,8 @@ static ITT_value itt_canon2(const mpq3 &p1, std::cout << "overlap [i [k l] j]\n"; } /* k is intersect with p2q2. l is intersect is p2r2. */ - intersect_1 = tti_interp(p2, q2, p1, n1); - intersect_2 = tti_interp(p2, r2, p1, n1); + intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]); } } else { @@ -1262,7 +1286,7 @@ static ITT_value itt_canon2(const mpq3 &p1, } else { /* Middle left test in classification tree. */ - if (tti_above(p1, q1, q2, p1p2) < 0) { + if (tti_above(p1, q1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) < 0) { /* No overlap: [i j] [k l]. */ if (dbg_level > 0) { std::cout << "no overlap: [i j] [k l]\n"; @@ -1271,14 +1295,14 @@ static ITT_value itt_canon2(const mpq3 &p1, } else { /* Bottom left test in classification tree. */ - if (tti_above(p1, r1, q2, p1p2) >= 0) { + if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) >= 0) { /* Overlap is [k [i j] l]. */ if (dbg_level > 0) { std::cout << "overlap [k [i j] l]\n"; } /* i is intersect with p1r1. j is intersect with p1q1. */ - intersect_1 = tti_interp(p1, r1, p2, n2); - intersect_2 = tti_interp(p1, q1, p2, n2); + intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]); } else { /* Overlap is [i [k j] l]. */ @@ -1286,8 +1310,8 @@ static ITT_value itt_canon2(const mpq3 &p1, std::cout << "overlap [i [k j] l]\n"; } /* k is intersect with p2q2. j is intersect with p1q1. */ - intersect_1 = tti_interp(p2, q2, p1, n1); - intersect_2 = tti_interp(p1, q1, p2, n2); + intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]); + intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]); } } } @@ -1438,6 +1462,7 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) return ITT_value(INONE); } + mpq3 buf[2]; const mpq3 &p1 = vp1->co_exact; const mpq3 &q1 = vq1->co_exact; const mpq3 &r1 = vr1->co_exact; @@ -1447,13 +1472,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) const mpq3 &n2 = tri2.plane->norm_exact; if (sp1 == 0) { - sp1 = sgn(mpq3::dot(p1 - r2, n2)); + buf[0] = p1; + buf[0] -= r2; + sp1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (sq1 == 0) { - sq1 = sgn(mpq3::dot(q1 - r2, n2)); + buf[0] = q1; + buf[0] -= r2; + sq1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (sr1 == 0) { - sr1 = sgn(mpq3::dot(r1 - r2, n2)); + buf[0] = r1; + buf[0] -= r2; + sr1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1])); } if (dbg_level > 1) { @@ -1473,13 +1504,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) /* Repeat for signs of t2's vertices with respect to plane of t1. */ const mpq3 &n1 = tri1.plane->norm_exact; if (sp2 == 0) { - sp2 = sgn(mpq3::dot(p2 - r1, n1)); + buf[0] = p2; + buf[0] -= r1; + sp2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (sq2 == 0) { - sq2 = sgn(mpq3::dot(q2 - r1, n1)); + buf[0] = q2; + buf[0] -= r1; + sq2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (sr2 == 0) { - sr2 = sgn(mpq3::dot(r2 - r1, n1)); + buf[0] = r2; + buf[0] -= r1; + sr2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1])); } if (dbg_level > 1) { -- cgit v1.2.3 From f253e5922150984ac2685d6ca4d3a29f7b25b1bb Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 31 May 2021 19:18:38 -0400 Subject: Docs: Limit the OCIO env vars that we document Brecht mentioned that these are a bit obscure and don't make much sense to override these. --- source/creator/creator_args.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index f9492f5bb62..8b1ac05f086 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -676,12 +676,6 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo printf(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n"); # ifdef WITH_OCIO printf(" $OCIO Path to override the OpenColorIO config file.\n"); - printf( - " $OCIO_ACTIVE_DISPLAYS Overrides the active_displays list from the config file and " - "reorders them. Colon-separated list of displays, e.g 'sRGB:P3'.\n"); - printf( - " $OCIO_ACTIVE_VIEWS Overrides the active_views list from the config file and " - "reorders them. Colon-separated list of view names, e.g 'internal:client:DI'.\n"); # endif # ifdef WIN32 printf(" $TEMP Store temporary files here.\n"); -- cgit v1.2.3 From c59d2c739d078f4a7a613a05995002cfdbdca303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 1 Jun 2021 01:45:17 +0200 Subject: Cleanup: spelling in comments --- intern/cycles/render/geometry.cpp | 2 +- intern/cycles/util/util_task.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 1c4b360750f..ce76658acb6 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -2063,7 +2063,7 @@ void GeometryManager::device_update(Device *device, * for meshes with correct bounding boxes. * * This wouldn't cause wrong results, just true - * displacement might be less optimal ot calculate. + * displacement might be less optimal to calculate. */ scene->object_manager->need_flags_update = old_need_object_flags_update; } diff --git a/intern/cycles/util/util_task.h b/intern/cycles/util/util_task.h index 7c39ed675b5..906bf420756 100644 --- a/intern/cycles/util/util_task.h +++ b/intern/cycles/util/util_task.h @@ -32,7 +32,7 @@ typedef function TaskRunFunction; /* Task Pool * - * Pool of tasks that will be executed by the central TaskScheduler.For each + * Pool of tasks that will be executed by the central TaskScheduler. For each * pool, we can wait for all tasks to be done, or cancel them before they are * done. * @@ -77,7 +77,7 @@ class TaskPool { /* Task Scheduler * - * Central scheduler that holds running threads ready to execute tasks. A singe + * Central scheduler that holds running threads ready to execute tasks. A single * queue holds the task from all pools. */ class TaskScheduler { -- cgit v1.2.3 From e7b8a3cb0a1429a642c14a2dabc77e3e20ecdf79 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:18 +1000 Subject: Cleanup: spelling in comments --- source/blender/blenkernel/intern/writeffmpeg.c | 12 ++++++------ source/blender/draw/intern/draw_hair.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 7c8eba20a28..60c216a8401 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -522,9 +522,9 @@ static AVRational calc_time_base(uint den, double num, int codec_id) { /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer * (within a floating point error range). - * For example if we have den = 3 and num = 0.1 then the fps is: den/num = 30 fps. - * When converthing this to a ffmpeg time base, we want num to be an integer. - * So we simply move the decimal places of both numbers. IE den = 30, num = 1.*/ + * For example if we have `den = 3` and `num = 0.1` then the fps is: `den/num = 30` fps. + * When converting this to a FFMPEG time base, we want num to be an integer. + * So we simply move the decimal places of both numbers. i.e. `den = 30`, `num = 1`. */ float eps = FLT_EPSILON; const uint DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; @@ -533,7 +533,7 @@ static AVRational calc_time_base(uint den, double num, int codec_id) const uint num_integer_bits = log2_floor_u((unsigned int)num); /* Formula for calculating the epsilon value: (power of two range) / (pow mantissa bits) - * For example, a float has 23 manitissa bits and the float value 3.5f as a pow2 range of + * For example, a float has 23 mantissa bits and the float value 3.5f as a pow2 range of * (4-2=2): * (2) / pow2(23) = floating point precision for 3.5f */ @@ -619,10 +619,10 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->time_base = calc_time_base(rd->frs_sec, rd->frs_sec_base, codec_id); } - /* As per the timebase documentation here: + /* As per the time-base documentation here: * https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options * We want to set the time base to (1 / fps) for fixed frame rate video. - * If it is not possible, we want to set the timebase numbers to something as + * If it is not possible, we want to set the time-base numbers to something as * small as possible. */ if (c->time_base.num != 1) { diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index bf3b10bccd3..585e171adc5 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -88,8 +88,8 @@ extern char datatoc_common_hair_refine_comp_glsl[]; extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; /* TODO(jbakker): move shader creation to `draw_shaders` and add test cases. */ -/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OSs. Currently the - * APPLE codepath does not compile on other platforms and vice versa. */ +/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OS's. + * Currently the `__APPLE__` code-path does not compile on other platforms and vice versa. */ #ifdef USE_COMPUTE_SHADERS static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement)) { -- cgit v1.2.3 From c145cb799897d7db89b1c75dc46fb71c33abfe00 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:20 +1000 Subject: Fix buffer overrun in paint_line_strokes_spacing Error in 87cafe92ce2f99d8da620b80e1c26f8078554f93 --- source/blender/editors/sculpt_paint/paint_stroke.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index b093f07226e..3f1fed5e5ea 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -1191,7 +1191,7 @@ static void paint_line_strokes_spacing(bContext *C, const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode); - float mouse[2], dmouse[2]; + float mouse[3], dmouse[2]; float length; float d_world_space_position[3] = {0.0f}; float world_space_position_old[3], world_space_position_new[3]; -- cgit v1.2.3 From f71cf996165a81030a92e3e26610bf2e6fa2fc44 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:20 +1000 Subject: GPU: add 2D projection function When projecting into screen space Z value isn't always needed. Add 2D projection functions, renaming them to avoid accidents happening again. - Add GPU_matrix_project_2fv - Add ED_view3d_project_v2 - Rename ED_view3d_project to ED_view3d_project_v3 - Use the 2D versions of these functions when the Z value isn't used. --- source/blender/editors/curve/editcurve_paint.c | 2 +- source/blender/editors/include/ED_view3d.h | 17 +++++--- source/blender/editors/mesh/editmesh_knife.c | 6 +-- source/blender/editors/object/object_remesh.c | 4 +- source/blender/editors/object/object_transform.c | 4 +- source/blender/editors/physics/particle_edit.c | 2 +- source/blender/editors/sculpt_paint/paint_cursor.c | 2 +- source/blender/editors/sculpt_paint/paint_stroke.c | 8 ++-- source/blender/editors/space_view3d/view3d_edit.c | 7 ++-- .../space_view3d/view3d_gizmo_preselect_type.c | 4 +- .../blender/editors/space_view3d/view3d_project.c | 15 +++++-- source/blender/editors/space_view3d/view3d_utils.c | 20 ++++----- source/blender/gpu/GPU_matrix.h | 36 ++++++++++------- source/blender/gpu/intern/gpu_matrix.cc | 47 +++++++++++++++------- .../windowmanager/gizmo/intern/wm_gizmo_map.c | 4 +- 15 files changed, 108 insertions(+), 70 deletions(-) diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 8842274e017..e4f2de1f741 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -210,7 +210,7 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd, ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl); const double depth = (double)depth_fl; if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { - if (ED_view3d_depth_unproject(region, mval_i, depth, r_location_world)) { + if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) { is_location_world_set = true; if (r_normal_world) { zero_v3(r_normal_world); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 66ec57c8a31..52d69d12253 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -162,10 +162,10 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd, bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, const int mval[2], float r_normal[3]); -bool ED_view3d_depth_unproject(const struct ARegion *region, - const int mval[2], - const double depth, - float r_location_world[3]); +bool ED_view3d_depth_unproject_v3(const struct ARegion *region, + const int mval[2], + const double depth, + float r_location_world[3]); void ED_view3d_depth_tag_update(struct RegionView3D *rv3d); /* Projection */ @@ -410,8 +410,13 @@ void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d, const float obmat[4][4], float r_pmat[4][4]); -void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]); -bool ED_view3d_unproject( +void ED_view3d_project_v3(const struct ARegion *region, + const float world[3], + float r_region_co[3]); +void ED_view3d_project_v2(const struct ARegion *region, + const float world[3], + float r_region_co[2]); +bool ED_view3d_unproject_v3( const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]); /* end */ diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index b5cd9c7f60d..825b7d11aef 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -578,8 +578,8 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd, float r_origin_ofs[3]) { /* unproject to find view ray */ - ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); - ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); + ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); + ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); /* transform into object space */ mul_m4_v3(kcd->ob_imat, r_origin); @@ -1745,7 +1745,7 @@ static bool point_is_visible(KnifeTool_OpData *kcd, float view[3], p_ofs[3]; /* TODO: I think there's a simpler way to get the required raycast ray */ - ED_view3d_unproject(kcd->vc.region, s[0], s[1], 0.0f, view); + ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view); mul_m4_v3(kcd->ob_imat, view); diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 9ef2cce875f..1ff576504ce 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -525,7 +525,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev float d_a[3], d_b[3]; float d_a_proj[2], d_b_proj[2]; - float preview_plane_proj[4][3]; + float preview_plane_proj[4][2]; const float y_axis_proj[2] = {0.0f, 1.0f}; mid_v3_v3v3(text_pos, cd->preview_plane[0], cd->preview_plane[2]); @@ -534,7 +534,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev for (int i = 0; i < 4; i++) { float preview_plane_world_space[3]; mul_v3_m4v3(preview_plane_world_space, active_object->obmat, cd->preview_plane[i]); - ED_view3d_project(region, preview_plane_world_space, preview_plane_proj[i]); + ED_view3d_project_v2(region, preview_plane_world_space, preview_plane_proj[i]); } /* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */ diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index a87b5054efa..b9a3bc87e19 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1654,7 +1654,7 @@ static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *x if (center_tot) { mul_v3_fl(center, 1.0f / center_tot); float center_proj[3]; - ED_view3d_project(xfd->vc.region, center, center_proj); + ED_view3d_project_v3(xfd->vc.region, center, center_proj); xfd->prev.depth = center_proj[2]; xfd->prev.is_depth_valid = true; } @@ -1890,7 +1890,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { xfd->prev.depth = depth_fl; xfd->prev.is_depth_valid = true; - if (ED_view3d_depth_unproject(region, event->mval, depth, location_world)) { + if (ED_view3d_depth_unproject_v3(region, event->mval, depth, location_world)) { if (is_translate) { float normal[3]; diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 5b545784e5b..97994b65f40 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -609,7 +609,7 @@ static bool key_test_depth(const PEData *data, const float co[3], const int scre } float win[3]; - ED_view3d_project(data->vc.region, co, win); + ED_view3d_project_v3(data->vc.region, co, win); if (win[2] - 0.00001f > depth) { return 0; diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 3829aeebbeb..7e111905883 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1039,7 +1039,7 @@ static void cursor_draw_point_screen_space(const uint gpuattr, float translation_vertex_cursor[3], location[3]; copy_v3_v3(location, true_location); mul_m4_v3(obmat, location); - ED_view3d_project(region, location, translation_vertex_cursor); + ED_view3d_project_v3(region, location, translation_vertex_cursor); /* Do not draw points behind the view. Z [near, far] is mapped to [-1, 1]. */ if (translation_vertex_cursor[2] <= 1.0f) { imm_draw_circle_fill_3d( diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 3f1fed5e5ea..59a5ad63f0e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -846,7 +846,7 @@ static int paint_space_stroke(bContext *C, while (length > 0.0f) { float spacing = paint_space_stroke_spacing_variable( C, scene, stroke, pressure, dpressure, length); - float mouse[3]; + float mouse[2]; if (length >= spacing) { if (use_scene_spacing) { @@ -856,7 +856,7 @@ static int paint_space_stroke(bContext *C, add_v3_v3v3(final_world_space_position, stroke->last_world_space_position, final_world_space_position); - ED_view3d_project(region, final_world_space_position, mouse); + ED_view3d_project_v2(region, final_world_space_position, mouse); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; @@ -1191,7 +1191,7 @@ static void paint_line_strokes_spacing(bContext *C, const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode); - float mouse[3], dmouse[2]; + float mouse[2], dmouse[2]; float length; float d_world_space_position[3] = {0.0f}; float world_space_position_old[3], world_space_position_new[3]; @@ -1240,7 +1240,7 @@ static void paint_line_strokes_spacing(bContext *C, mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing_final); add_v3_v3v3( final_world_space_position, world_space_position_old, final_world_space_position); - ED_view3d_project(region, final_world_space_position, mouse); + ED_view3d_project_v2(region, final_world_space_position, mouse); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 967ad966320..8b6d0e9ee04 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -3666,8 +3666,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } /* convert border to 3d coordinates */ - if ((!ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) || - (!ED_view3d_unproject(region, rect.xmin, rect.ymin, depth_close, p_corner))) { + if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) || + (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) { return OPERATOR_CANCELLED; } @@ -3690,7 +3690,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) new_dist = rv3d->dist; /* convert the drawn rectangle into 3d space */ - if (depth_close != FLT_MAX && ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) { + if (depth_close != FLT_MAX && + ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) { negate_v3_v3(new_ofs, p); } else { diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c index 298a2a7a824..07c3b6bd1d8 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -154,10 +154,10 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int * Only pre-select a vertex when the cursor is really close to it. */ if (eve_test) { BMVert *vert = (BMVert *)eve_test; - float vert_p_co[3], vert_co[3]; + float vert_p_co[2], vert_co[3]; const float mval_f[2] = {UNPACK2(vc.mval)}; mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co); - ED_view3d_project(vc.region, vert_co, vert_p_co); + ED_view3d_project_v2(vc.region, vert_co, vert_p_co); float len = len_v2v2(vert_p_co, mval_f); if (len < 35) { best.ele = (BMElem *)eve_test; diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 24d34e514c5..7547f8ee434 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -809,23 +809,30 @@ void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, /** * Convert between region relative coordinates (x,y) and depth component z and * a point in world space. */ -void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]) +void ED_view3d_project_v3(const struct ARegion *region, const float world[3], float r_region_co[3]) { /* Viewport is set up to make coordinates relative to the region, not window. */ RegionView3D *rv3d = region->regiondata; const int viewport[4] = {0, 0, region->winx, region->winy}; + GPU_matrix_project_3fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); +} - GPU_matrix_project(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); +void ED_view3d_project_v2(const struct ARegion *region, const float world[3], float r_region_co[2]) +{ + /* Viewport is set up to make coordinates relative to the region, not window. */ + RegionView3D *rv3d = region->regiondata; + const int viewport[4] = {0, 0, region->winx, region->winy}; + GPU_matrix_project_2fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co); } -bool ED_view3d_unproject( +bool ED_view3d_unproject_v3( const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]) { RegionView3D *rv3d = region->regiondata; const int viewport[4] = {0, 0, region->winx, region->winy}; const float region_co[3] = {regionx, regiony, regionz}; - return GPU_matrix_unproject(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); + return GPU_matrix_unproject_3fv(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index f96c17d7cff..8ae5d4a29e9 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -299,8 +299,8 @@ void ED_view3d_clipping_calc( float xs = (ELEM(val, 0, 3)) ? rect->xmin : rect->xmax; float ys = (ELEM(val, 0, 1)) ? rect->ymin : rect->ymax; - ED_view3d_unproject(region, xs, ys, 0.0, bb->vec[val]); - ED_view3d_unproject(region, xs, ys, 1.0, bb->vec[4 + val]); + ED_view3d_unproject_v3(region, xs, ys, 0.0, bb->vec[val]); + ED_view3d_unproject_v3(region, xs, ys, 1.0, bb->vec[4 + val]); } /* optionally transform to object space */ @@ -1057,7 +1057,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph, float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - if (ED_view3d_unproject(region, centx, centy, depth_close, mouse_worldloc)) { + if (ED_view3d_unproject_v3(region, centx, centy, depth_close, mouse_worldloc)) { return true; } } @@ -1091,7 +1091,7 @@ bool ED_view3d_autodist_simple(ARegion *region, float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - return ED_view3d_unproject(region, centx, centy, depth, mouse_worldloc); + return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc); } bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth) @@ -1716,7 +1716,7 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, ED_view3d_depth_read_cached(depths, mval_ofs, 0, &depth_fl); const double depth = (double)depth_fl; if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { - if (ED_view3d_depth_unproject(region, mval_ofs, depth, coords[i])) { + if (ED_view3d_depth_unproject_v3(region, mval_ofs, depth, coords[i])) { depths_valid[i] = true; } } @@ -1751,14 +1751,14 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc, return false; } -bool ED_view3d_depth_unproject(const ARegion *region, - const int mval[2], - const double depth, - float r_location_world[3]) +bool ED_view3d_depth_unproject_v3(const ARegion *region, + const int mval[2], + const double depth, + float r_location_world[3]) { float centx = (float)mval[0] + 0.5f; float centy = (float)mval[1] + 0.5f; - return ED_view3d_unproject(region, centx, centy, depth, r_location_world); + return ED_view3d_unproject_v3(region, centx, centy, depth, r_location_world); } void ED_view3d_depth_tag_update(RegionView3D *rv3d) diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index aad6ae9e2ba..e073263f352 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -118,21 +118,27 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *unproj_prec const float proj[4][4], const int view[4]); -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_win[3]); - -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]); - -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, - const float win[3], - float r_world[3]); +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[3]); + +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[2]); + +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]); + +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, + const float win[3], + float r_world[3]); /* 2D Projection Matrix */ diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 569b51a407a..6eb9cb823d5 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -474,11 +474,11 @@ void GPU_matrix_look_at(float eyeX, GPU_matrix_translate_3f(-eyeX, -eyeY, -eyeZ); } -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float win[3]) +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[3]) { float v[4]; @@ -494,6 +494,25 @@ void GPU_matrix_project(const float world[3], win[2] = (v[2] + 1) * 0.5f; } +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[2]) +{ + float v[4]; + + mul_v4_m4v3(v, model, world); + mul_m4_v4(proj, v); + + if (v[3] != 0.0f) { + mul_v2_fl(v, 1.0f / v[3]); + } + + win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f; + win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f; +} + /** * The same result could be obtained as follows: * @@ -556,9 +575,9 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, return true; } -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, - const float win[3], - float r_world[3]) +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, + const float win[3], + float r_world[3]) { float in[3] = { (win[0] - precalc->view[0]) / precalc->view[2], @@ -569,18 +588,18 @@ void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc * mul_v3_m4v3(r_world, precalc->model_inverted, in); } -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]) +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]) { struct GPUMatrixUnproject_Precalc precalc; if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) { zero_v3(r_world); return false; } - GPU_matrix_unproject_with_precalc(&precalc, win, r_world); + GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world); return true; } diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 611a9cba000..2ffa04bd8ae 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -620,7 +620,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, struct GPUMatrixUnproject_Precalc unproj_precalc; GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport); - GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin); + GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin); uint *buf_iter = buffer; int hit_found = -1; @@ -631,7 +631,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8]; float co_3d[3]; co_screen[2] = int_as_float(buf_iter[1]); - GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d); + GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d); float select_bias = gz->select_bias; if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) { select_bias *= gz->scale_final; -- cgit v1.2.3 From f8ce744c83500b86dad4d5a62a0b733bf9fd4b59 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:21 +1000 Subject: Cleanup: correct sculpt quat argument size --- source/blender/editors/sculpt_paint/sculpt.c | 4 ++-- source/blender/editors/sculpt_paint/sculpt_intern.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 2e1dd928f96..d6d54a1985d 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1854,7 +1854,7 @@ static void flip_v3(float v[3], const ePaintSymmetryFlags symm) flip_v3_v3(v, v, symm); } -static void flip_qt(float quat[3], const ePaintSymmetryFlags symm) +static void flip_qt(float quat[4], const ePaintSymmetryFlags symm) { flip_qt_qt(quat, quat, symm); } @@ -4196,7 +4196,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3], } } -void SCULPT_flip_quat_by_symm_area(float quat[3], +void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]) diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 087cb6dd94a..e2ee4c9fed3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -278,7 +278,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]); -void SCULPT_flip_quat_by_symm_area(float quat[3], +void SCULPT_flip_quat_by_symm_area(float quat[4], const ePaintSymmetryFlags symm, const ePaintSymmetryAreas symmarea, const float pivot[3]); -- cgit v1.2.3 From 5e7fb77dc4d27df74a8e839b8a4eb7a1fe43475b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:22 +1000 Subject: BMesh: remove checks for tessellating 2 sided faces 2 sided faces aren't supported and will cause problems in many areas of Blender's code. Removing (implied) support for faces with fewer than 3 sides means the total number of triangles is known ahead of time. This simplifies adding support for multi-threading and partial updates to an existing tessellation - as the face and loop indices can be used to access the range of triangles associated with a face. Also correct outdated comments. --- source/blender/blenkernel/intern/editmesh.c | 3 ++- source/blender/bmesh/intern/bmesh_polygon.c | 28 +++++++--------------- source/blender/bmesh/intern/bmesh_polygon.h | 4 ++-- source/blender/editors/sculpt_paint/paint_mask.c | 5 ++-- source/blender/modifiers/intern/MOD_boolean.cc | 5 ++-- source/blender/python/bmesh/bmesh_py_types.c | 7 +++--- .../blender/python/mathutils/mathutils_bvhtree.c | 5 +--- 7 files changed, 21 insertions(+), 36 deletions(-) diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 0fa3bb29ccd..bd76357617a 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -127,9 +127,10 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em) } em->looptris = looptris; + em->tottri = looptris_tot; /* after allocating the em->looptris, we're ready to tessellate */ - BM_mesh_calc_tessellation(em->bm, em->looptris, &em->tottri); + BM_mesh_calc_tessellation(em->bm, em->looptris); } void BKE_editmesh_looptri_calc(BMEditMesh *em) diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 4ae2cc67140..5e6635cc929 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -1530,14 +1530,11 @@ void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) * * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count */ -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot) +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) { - /* use this to avoid locking pthread for _every_ polygon - * and calling the fill function */ + /* Avoid polygon filling logic for 3-4 sided faces. */ #define USE_TESSFACE_SPEEDUP - /* this assumes all faces can be scan-filled, which isn't always true, - * worst case we over alloc a little which is acceptable */ #ifndef NDEBUG const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); #endif @@ -1549,9 +1546,10 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptri MemArena *arena = NULL; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); /* don't consider two-edged faces */ - if (UNLIKELY(efa->len < 3)) { - /* do nothing */ + if (0) { + /* do nothing (needed for else statements below) */ } #ifdef USE_TESSFACE_SPEEDUP @@ -1664,8 +1662,6 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptri arena = NULL; } - *r_looptris_tot = i; - BLI_assert(i <= looptris_tot); #undef USE_TESSFACE_SPEEDUP @@ -1674,10 +1670,8 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptri /** * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. */ -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot) +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) { - /* this assumes all faces can be scan-filled, which isn't always true, - * worst case we over alloc a little which is acceptable */ #ifndef NDEBUG const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); #endif @@ -1692,11 +1686,9 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_ Heap *pf_heap = NULL; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - /* don't consider two-edged faces */ - if (UNLIKELY(efa->len < 3)) { - /* do nothing */ - } - else if (efa->len == 3) { + BLI_assert(efa->len >= 3); + + if (efa->len == 3) { BMLoop *l; BMLoop **l_ptr = looptris[i++]; l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); @@ -1805,7 +1797,5 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_ BLI_heap_free(pf_heap, NULL); } - *r_looptris_tot = i; - BLI_assert(i <= looptris_tot); } diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 8c2b9ee0bff..48837d4d0c6 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -24,8 +24,8 @@ struct Heap; #include "BLI_compiler_attrs.h" -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); void BM_face_calc_tessellation(const BMFace *f, const bool use_fixed_quad, diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index b6ae6f8bee7..da34723eed4 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1239,10 +1239,9 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) })); const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - int tottri; BMLoop *(*looptris)[3]; looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); - BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + BM_mesh_calc_tessellation_beauty(bm, looptris); BMIter iter; int i; @@ -1290,7 +1289,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) break; } BM_mesh_boolean( - bm, looptris, tottri, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode); + bm, looptris, looptris_tot, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode); } MEM_freeN(looptris); diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 9b8782737c3..4b9b24e4e47 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -283,11 +283,10 @@ static void BMD_mesh_intersection(BMesh *bm, /* main bmesh intersection setup */ /* create tessface & intersect */ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); - int tottri; BMLoop *(*looptris)[3] = (BMLoop * (*)[3]) MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__); - BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri); + BM_mesh_calc_tessellation_beauty(bm, looptris); /* postpone this until after tessellating * so we can use the original normals before the vertex are moved */ @@ -364,7 +363,7 @@ static void BMD_mesh_intersection(BMesh *bm, BM_mesh_intersect(bm, looptris, - tottri, + looptris_tot, bm_face_isect_pair, nullptr, false, diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index f1a8d450ea5..598640f8f68 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1385,7 +1385,6 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self) BMesh *bm; int looptris_tot; - int tottri; BMLoop *(*looptris)[3]; PyObject *ret; @@ -1398,10 +1397,10 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self) looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); looptris = PyMem_MALLOC(sizeof(*looptris) * looptris_tot); - BM_mesh_calc_tessellation(bm, looptris, &tottri); + BM_mesh_calc_tessellation(bm, looptris); - ret = PyList_New(tottri); - for (i = 0; i < tottri; i++) { + ret = PyList_New(looptris_tot); + for (i = 0; i < looptris_tot; i++) { PyList_SET_ITEM(ret, i, BPy_BMLoop_Array_As_Tuple(bm, looptris[i], 3)); } diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index 1acbcc006ca..79ed9e68420 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -961,8 +961,6 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb /* Get data for tessellation */ { - int tris_len_dummy; - coords_len = (uint)bm->totvert; tris_len = (uint)poly_to_tri_count(bm->totface, bm->totloop); @@ -971,8 +969,7 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb looptris = MEM_mallocN(sizeof(*looptris) * (size_t)tris_len, __func__); - BM_mesh_calc_tessellation(bm, looptris, &tris_len_dummy); - BLI_assert(tris_len_dummy == (int)tris_len); + BM_mesh_calc_tessellation(bm, looptris); } { -- cgit v1.2.3 From 3a18e304be5ce6f800b997879a3c5c0fe357b330 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:49:22 +1000 Subject: Cleanup: remove disabled face tessellation logic This was kept since these blocks are easier to follow. Remove as the overall result wasn't so readable (especially with nested ifdef's). Replace disabled code with comment on the indices used for quads/tris. --- source/blender/bmesh/intern/bmesh_polygon.c | 46 ++++------------------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 5e6635cc929..f1e6da9b746 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -1547,51 +1547,17 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { BLI_assert(efa->len >= 3); - /* don't consider two-edged faces */ - if (0) { - /* do nothing (needed for else statements below) */ - } - #ifdef USE_TESSFACE_SPEEDUP - - /* no need to ensure the loop order, we know its ok */ - - else if (efa->len == 3) { -# if 0 - int j; - BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) { - looptris[i][j] = l; - } - i += 1; -# else - /* more cryptic but faster */ + if (efa->len == 3) { + /* `0 1 2` -> `0 1 2` */ BMLoop *l; BMLoop **l_ptr = looptris[i++]; l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); l_ptr[1] = l = l->next; l_ptr[2] = l->next; -# endif } else if (efa->len == 4) { -# if 0 - BMLoop *ltmp[4]; - int j; - BLI_array_grow_items(looptris, 2); - BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) { - ltmp[j] = l; - } - - looptris[i][0] = ltmp[0]; - looptris[i][1] = ltmp[1]; - looptris[i][2] = ltmp[2]; - i += 1; - - looptris[i][0] = ltmp[0]; - looptris[i][1] = ltmp[2]; - looptris[i][2] = ltmp[3]; - i += 1; -# else - /* more cryptic but faster */ + /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ BMLoop *l; BMLoop **l_ptr_a = looptris[i++]; BMLoop **l_ptr_b = looptris[i++]; @@ -1599,7 +1565,6 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) (l_ptr_a[1] = l = l->next); (l_ptr_a[2] = l_ptr_b[1] = l = l->next); (l_ptr_b[2] = l->next); -# endif if (UNLIKELY(is_quad_flip_v3_first_third_fast( l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { @@ -1608,10 +1573,9 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) l_ptr_b[0] = l_ptr_a[1]; } } - + else #endif /* USE_TESSFACE_SPEEDUP */ - - else { + { int j; BMLoop *l_iter; -- cgit v1.2.3 From b8d0f28f700ff6bbba5a03d69f19f6931981bbc5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 12:58:52 +1000 Subject: Cleanup: split bmesh tessellation into it's own file Prepare for further refactoring for these functions. --- source/blender/bmesh/CMakeLists.txt | 2 + source/blender/bmesh/bmesh.h | 1 + .../blender/bmesh/intern/bmesh_mesh_tessellate.c | 277 +++++++++++++++++++++ .../blender/bmesh/intern/bmesh_mesh_tessellate.h | 24 ++ source/blender/bmesh/intern/bmesh_polygon.c | 243 +----------------- source/blender/bmesh/intern/bmesh_polygon.h | 3 - 6 files changed, 305 insertions(+), 245 deletions(-) create mode 100644 source/blender/bmesh/intern/bmesh_mesh_tessellate.c create mode 100644 source/blender/bmesh/intern/bmesh_mesh_tessellate.h diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 92064b3d040..af47b9557ef 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -102,6 +102,8 @@ set(SRC intern/bmesh_mesh_convert.h intern/bmesh_mesh_duplicate.c intern/bmesh_mesh_duplicate.h + intern/bmesh_mesh_tessellate.c + intern/bmesh_mesh_tessellate.h intern/bmesh_mesh_validate.c intern/bmesh_mesh_validate.h intern/bmesh_mods.c diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index 4441ccc0c88..a7e4e011ae2 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -215,6 +215,7 @@ extern "C" { #include "intern/bmesh_mesh.h" #include "intern/bmesh_mesh_convert.h" #include "intern/bmesh_mesh_duplicate.h" +#include "intern/bmesh_mesh_tessellate.h" #include "intern/bmesh_mesh_validate.h" #include "intern/bmesh_mods.h" #include "intern/bmesh_operators.h" diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c new file mode 100644 index 00000000000..bde2c75a079 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c @@ -0,0 +1,277 @@ +/* + * 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. + */ + +/** \file + * \ingroup bmesh + * + * This file contains code for polygon tessellation + * (creating triangles from polygons). + */ + +#include "DNA_meshdata_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_heap.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" +#include "BLI_polyfill_2d_beautify.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +/** + * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh + * \param looptris: + * + * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count + */ +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) +{ + /* Avoid polygon filling logic for 3-4 sided faces. */ +#define USE_TESSFACE_SPEEDUP + +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *arena = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); +#ifdef USE_TESSFACE_SPEEDUP + if (efa->len == 3) { + /* `0 1 2` -> `0 1 2` */ + BMLoop *l; + BMLoop **l_ptr = looptris[i++]; + l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); + l_ptr[1] = l = l->next; + l_ptr[2] = l->next; + } + else if (efa->len == 4) { + /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ + BMLoop *l; + BMLoop **l_ptr_a = looptris[i++]; + BMLoop **l_ptr_b = looptris[i++]; + (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); + (l_ptr_a[1] = l = l->next); + (l_ptr_a[2] = l_ptr_b[1] = l = l->next); + (l_ptr_b[2] = l->next); + + if (UNLIKELY(is_quad_flip_v3_first_third_fast( + l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { + /* flip out of degenerate 0-2 state. */ + l_ptr_a[2] = l_ptr_b[2]; + l_ptr_b[0] = l_ptr_a[1]; + } + } + else +#endif /* USE_TESSFACE_SPEEDUP */ + { + int j; + + BMLoop *l_iter; + BMLoop *l_first; + BMLoop **l_arr; + + float axis_mat[3][3]; + float(*projverts)[2]; + uint(*tris)[3]; + + const int totfilltri = efa->len - 2; + + if (UNLIKELY(arena == NULL)) { + arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri); + l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len); + + axis_dominant_v3_to_m3_negate(axis_mat, efa->no); + + j = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + l_arr[j] = l_iter; + mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); + j++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena); + + for (j = 0; j < totfilltri; j++) { + BMLoop **l_ptr = looptris[i++]; + uint *tri = tris[j]; + + l_ptr[0] = l_arr[tri[0]]; + l_ptr[1] = l_arr[tri[1]]; + l_ptr[2] = l_arr[tri[2]]; + } + + BLI_memarena_clear(arena); + } + } + + if (arena) { + BLI_memarena_free(arena); + arena = NULL; + } + + BLI_assert(i <= looptris_tot); + +#undef USE_TESSFACE_SPEEDUP +} + +/** + * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. + */ +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) +{ +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *pf_arena = NULL; + + /* use_beauty */ + Heap *pf_heap = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + + if (efa->len == 3) { + BMLoop *l; + BMLoop **l_ptr = looptris[i++]; + l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); + l_ptr[1] = l = l->next; + l_ptr[2] = l->next; + } + else if (efa->len == 4) { + BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); + BMLoop *l_v2 = l_v1->next; + BMLoop *l_v3 = l_v2->next; + BMLoop *l_v4 = l_v1->prev; + + /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need! + * It's meant for rotating edges, it also calculates a new normal. + * + * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal. + */ +#if 0 + const bool split_13 = (BM_verts_calc_rotate_beauty( + l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f); +#else + float axis_mat[3][3], v_quad[4][2]; + axis_dominant_v3_to_m3(axis_mat, efa->no); + mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co); + mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co); + mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co); + mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co); + + const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc( + v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; +#endif + + BMLoop **l_ptr_a = looptris[i++]; + BMLoop **l_ptr_b = looptris[i++]; + if (split_13) { + l_ptr_a[0] = l_v1; + l_ptr_a[1] = l_v2; + l_ptr_a[2] = l_v3; + + l_ptr_b[0] = l_v1; + l_ptr_b[1] = l_v3; + l_ptr_b[2] = l_v4; + } + else { + l_ptr_a[0] = l_v1; + l_ptr_a[1] = l_v2; + l_ptr_a[2] = l_v4; + + l_ptr_b[0] = l_v2; + l_ptr_b[1] = l_v3; + l_ptr_b[2] = l_v4; + } + } + else { + int j; + + BMLoop *l_iter; + BMLoop *l_first; + BMLoop **l_arr; + + float axis_mat[3][3]; + float(*projverts)[2]; + unsigned int(*tris)[3]; + + const int totfilltri = efa->len - 2; + + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); + } + + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri); + l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); + + axis_dominant_v3_to_m3_negate(axis_mat, efa->no); + + j = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + l_arr[j] = l_iter; + mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); + j++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); + + BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); + + for (j = 0; j < totfilltri; j++) { + BMLoop **l_ptr = looptris[i++]; + unsigned int *tri = tris[j]; + + l_ptr[0] = l_arr[tri[0]]; + l_ptr[1] = l_arr[tri[1]]; + l_ptr[2] = l_arr[tri[2]]; + } + + BLI_memarena_clear(pf_arena); + } + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + + BLI_heap_free(pf_heap, NULL); + } + + BLI_assert(i <= looptris_tot); +} diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h new file mode 100644 index 00000000000..5606a5a7e02 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h @@ -0,0 +1,24 @@ +/* + * 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 bmesh + */ + +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index f1e6da9b746..5397098a7f3 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -18,8 +18,7 @@ * \ingroup bmesh * * This file contains code for dealing - * with polygons (normal/area calculation, - * tessellation, etc) + * with polygons (normal/area calculation, tessellation, etc) */ #include "DNA_listBase.h" @@ -1523,243 +1522,3 @@ void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) l = l->next; r_loops[3] = l; } - -/** - * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh - * \param looptris: - * - * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count - */ -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) -{ - /* Avoid polygon filling logic for 3-4 sided faces. */ -#define USE_TESSFACE_SPEEDUP - -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *arena = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BLI_assert(efa->len >= 3); -#ifdef USE_TESSFACE_SPEEDUP - if (efa->len == 3) { - /* `0 1 2` -> `0 1 2` */ - BMLoop *l; - BMLoop **l_ptr = looptris[i++]; - l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); - l_ptr[1] = l = l->next; - l_ptr[2] = l->next; - } - else if (efa->len == 4) { - /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ - BMLoop *l; - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; - (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); - (l_ptr_a[1] = l = l->next); - (l_ptr_a[2] = l_ptr_b[1] = l = l->next); - (l_ptr_b[2] = l->next); - - if (UNLIKELY(is_quad_flip_v3_first_third_fast( - l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { - /* flip out of degenerate 0-2 state. */ - l_ptr_a[2] = l_ptr_b[2]; - l_ptr_b[0] = l_ptr_a[1]; - } - } - else -#endif /* USE_TESSFACE_SPEEDUP */ - { - int j; - - BMLoop *l_iter; - BMLoop *l_first; - BMLoop **l_arr; - - float axis_mat[3][3]; - float(*projverts)[2]; - uint(*tris)[3]; - - const int totfilltri = efa->len - 2; - - if (UNLIKELY(arena == NULL)) { - arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - } - - tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len); - - axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - - j = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; - } while ((l_iter = l_iter->next) != l_first); - - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena); - - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - uint *tri = tris[j]; - - l_ptr[0] = l_arr[tri[0]]; - l_ptr[1] = l_arr[tri[1]]; - l_ptr[2] = l_arr[tri[2]]; - } - - BLI_memarena_clear(arena); - } - } - - if (arena) { - BLI_memarena_free(arena); - arena = NULL; - } - - BLI_assert(i <= looptris_tot); - -#undef USE_TESSFACE_SPEEDUP -} - -/** - * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. - */ -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) -{ -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *pf_arena = NULL; - - /* use_beauty */ - Heap *pf_heap = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BLI_assert(efa->len >= 3); - - if (efa->len == 3) { - BMLoop *l; - BMLoop **l_ptr = looptris[i++]; - l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); - l_ptr[1] = l = l->next; - l_ptr[2] = l->next; - } - else if (efa->len == 4) { - BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); - BMLoop *l_v2 = l_v1->next; - BMLoop *l_v3 = l_v2->next; - BMLoop *l_v4 = l_v1->prev; - - /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need! - * It's meant for rotating edges, it also calculates a new normal. - * - * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal. - */ -#if 0 - const bool split_13 = (BM_verts_calc_rotate_beauty( - l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f); -#else - float axis_mat[3][3], v_quad[4][2]; - axis_dominant_v3_to_m3(axis_mat, efa->no); - mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co); - mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co); - mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co); - mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co); - - const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc( - v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; -#endif - - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; - if (split_13) { - l_ptr_a[0] = l_v1; - l_ptr_a[1] = l_v2; - l_ptr_a[2] = l_v3; - - l_ptr_b[0] = l_v1; - l_ptr_b[1] = l_v3; - l_ptr_b[2] = l_v4; - } - else { - l_ptr_a[0] = l_v1; - l_ptr_a[1] = l_v2; - l_ptr_a[2] = l_v4; - - l_ptr_b[0] = l_v2; - l_ptr_b[1] = l_v3; - l_ptr_b[2] = l_v4; - } - } - else { - int j; - - BMLoop *l_iter; - BMLoop *l_first; - BMLoop **l_arr; - - float axis_mat[3][3]; - float(*projverts)[2]; - unsigned int(*tris)[3]; - - const int totfilltri = efa->len - 2; - - if (UNLIKELY(pf_arena == NULL)) { - pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); - } - - tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); - - axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - - j = 0; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; - } while ((l_iter = l_iter->next) != l_first); - - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); - - BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); - - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - unsigned int *tri = tris[j]; - - l_ptr[0] = l_arr[tri[0]]; - l_ptr[1] = l_arr[tri[1]]; - l_ptr[2] = l_arr[tri[2]]; - } - - BLI_memarena_clear(pf_arena); - } - } - - if (pf_arena) { - BLI_memarena_free(pf_arena); - - BLI_heap_free(pf_heap, NULL); - } - - BLI_assert(i <= looptris_tot); -} diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 48837d4d0c6..e7d5cb2f89d 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -24,9 +24,6 @@ struct Heap; #include "BLI_compiler_attrs.h" -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); - void BM_face_calc_tessellation(const BMFace *f, const bool use_fixed_quad, BMLoop **r_loops, -- cgit v1.2.3 From 13deb5088a4dd58603661f8643bbddab1c75b516 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 1 Jun 2021 15:20:35 +1000 Subject: Cleanup: split face tessellation into inline functions Prepare for multiple code-paths that recalculate tessellation. --- .../blender/bmesh/intern/bmesh_mesh_tessellate.c | 206 ++++++++++++--------- 1 file changed, 117 insertions(+), 89 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c index bde2c75a079..c8ea9429145 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c @@ -36,43 +36,29 @@ #include "bmesh.h" #include "bmesh_tools.h" -/** - * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh - * \param looptris: - * - * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count - */ -void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) -{ - /* Avoid polygon filling logic for 3-4 sided faces. */ -#define USE_TESSFACE_SPEEDUP +/* -------------------------------------------------------------------- */ +/** \name Default Mesh Tessellation + * \{ */ -#ifndef NDEBUG - const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); -#endif - - BMIter iter; - BMFace *efa; - int i = 0; - - MemArena *arena = NULL; - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BLI_assert(efa->len >= 3); -#ifdef USE_TESSFACE_SPEEDUP - if (efa->len == 3) { +static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p) +{ + switch (efa->len) { + case 3: { /* `0 1 2` -> `0 1 2` */ BMLoop *l; - BMLoop **l_ptr = looptris[i++]; + BMLoop **l_ptr = looptris[0]; l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); l_ptr[1] = l = l->next; l_ptr[2] = l->next; + return 1; } - else if (efa->len == 4) { + case 4: { /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */ BMLoop *l; - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; + BMLoop **l_ptr_a = looptris[0]; + BMLoop **l_ptr_b = looptris[1]; (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa)); (l_ptr_a[1] = l = l->next); (l_ptr_a[2] = l_ptr_b[1] = l = l->next); @@ -80,73 +66,65 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) if (UNLIKELY(is_quad_flip_v3_first_third_fast( l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) { - /* flip out of degenerate 0-2 state. */ + /* Flip out of degenerate 0-2 state. */ l_ptr_a[2] = l_ptr_b[2]; l_ptr_b[0] = l_ptr_a[1]; } + return 2; } - else -#endif /* USE_TESSFACE_SPEEDUP */ - { - int j; - - BMLoop *l_iter; - BMLoop *l_first; + default: { + BMLoop *l_iter, *l_first; BMLoop **l_arr; float axis_mat[3][3]; float(*projverts)[2]; uint(*tris)[3]; - const int totfilltri = efa->len - 2; + const int tris_len = efa->len - 2; - if (UNLIKELY(arena == NULL)) { - arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + MemArena *pf_arena = *pf_arena_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); } - tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri); - l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len); - projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len); + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len); + l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); + projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - j = 0; + int i = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(efa); do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; + l_arr[i] = l_iter; + mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); + i++; } while ((l_iter = l_iter->next) != l_first); - BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena); + BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - uint *tri = tris[j]; + for (i = 0; i < tris_len; i++) { + BMLoop **l_ptr = looptris[i]; + uint *tri = tris[i]; l_ptr[0] = l_arr[tri[0]]; l_ptr[1] = l_arr[tri[1]]; l_ptr[2] = l_arr[tri[2]]; } - BLI_memarena_clear(arena); + BLI_memarena_clear(pf_arena); + return tris_len; } } - - if (arena) { - BLI_memarena_free(arena); - arena = NULL; - } - - BLI_assert(i <= looptris_tot); - -#undef USE_TESSFACE_SPEEDUP } /** - * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. + * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh + * \param looptris: + * + * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count */ -void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) +void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) { #ifndef NDEBUG const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); @@ -158,20 +136,42 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) MemArena *pf_arena = NULL; - /* use_beauty */ - Heap *pf_heap = NULL; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { BLI_assert(efa->len >= 3); + i += mesh_calc_tessellation_for_face(looptris + i, efa, &pf_arena); + } - if (efa->len == 3) { + if (pf_arena) { + BLI_memarena_free(pf_arena); + pf_arena = NULL; + } + + BLI_assert(i <= looptris_tot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Beauty Mesh Tessellation + * + * Avoid degenerate triangles. + * \{ */ + +static int mesh_calc_tessellation_for_face_beauty(BMLoop *(*looptris)[3], + BMFace *efa, + MemArena **pf_arena_p, + Heap **pf_heap_p) +{ + switch (efa->len) { + case 3: { BMLoop *l; - BMLoop **l_ptr = looptris[i++]; + BMLoop **l_ptr = looptris[0]; l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa); l_ptr[1] = l = l->next; l_ptr[2] = l->next; + return 1; } - else if (efa->len == 4) { + case 4: { BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa); BMLoop *l_v2 = l_v1->next; BMLoop *l_v3 = l_v2->next; @@ -197,8 +197,8 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f; #endif - BMLoop **l_ptr_a = looptris[i++]; - BMLoop **l_ptr_b = looptris[i++]; + BMLoop **l_ptr_a = looptris[0]; + BMLoop **l_ptr_b = looptris[1]; if (split_13) { l_ptr_a[0] = l_v1; l_ptr_a[1] = l_v2; @@ -217,46 +217,46 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) l_ptr_b[1] = l_v3; l_ptr_b[2] = l_v4; } + return 2; } - else { - int j; + default: { + MemArena *pf_arena = *pf_arena_p; + Heap *pf_heap = *pf_heap_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + pf_heap = *pf_heap_p = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); + } - BMLoop *l_iter; - BMLoop *l_first; + BMLoop *l_iter, *l_first; BMLoop **l_arr; float axis_mat[3][3]; float(*projverts)[2]; - unsigned int(*tris)[3]; - - const int totfilltri = efa->len - 2; + uint(*tris)[3]; - if (UNLIKELY(pf_arena == NULL)) { - pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); - } + const int tris_len = efa->len - 2; - tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri); + tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len); l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len); projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len); axis_dominant_v3_to_m3_negate(axis_mat, efa->no); - j = 0; + int i = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(efa); do { - l_arr[j] = l_iter; - mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co); - j++; + l_arr[i] = l_iter; + mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co); + i++; } while ((l_iter = l_iter->next) != l_first); BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena); BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap); - for (j = 0; j < totfilltri; j++) { - BMLoop **l_ptr = looptris[i++]; - unsigned int *tri = tris[j]; + for (i = 0; i < tris_len; i++) { + BMLoop **l_ptr = looptris[i]; + uint *tri = tris[i]; l_ptr[0] = l_arr[tri[0]]; l_ptr[1] = l_arr[tri[1]]; @@ -264,8 +264,34 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) } BLI_memarena_clear(pf_arena); + + return tris_len; } } +} + +/** + * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles. + */ +void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) +{ +#ifndef NDEBUG + const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); +#endif + + BMIter iter; + BMFace *efa; + int i = 0; + + MemArena *pf_arena = NULL; + + /* use_beauty */ + Heap *pf_heap = NULL; + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BLI_assert(efa->len >= 3); + i += mesh_calc_tessellation_for_face_beauty(looptris + i, efa, &pf_arena, &pf_heap); + } if (pf_arena) { BLI_memarena_free(pf_arena); @@ -275,3 +301,5 @@ void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]) BLI_assert(i <= looptris_tot); } + +/** \} */ -- cgit v1.2.3 From 930ad9257d00a1891a948ff71756ffe8acb61686 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 09:23:37 +0200 Subject: Cleanup: Split draw_cache_extract_mesh into multiple files. draw_cache_extract_mesh for task scheduling. Will be refactored to draw_cache_extract_mesh_scheduling later on after migrating to CPP. draw_cache_extract_mesh_render_data extraction of mesh render data from edit mesh/mesh into a more generic structure. draw_cache_extract_mesh_extractors containing all the extractors. This will be split up further into a single file per extractor. --- source/blender/draw/CMakeLists.txt | 2 + source/blender/draw/intern/draw_cache_extract.h | 10 +- .../blender/draw/intern/draw_cache_extract_mesh.c | 5608 +------------------- .../intern/draw_cache_extract_mesh_extractors.c | 4748 +++++++++++++++++ .../draw/intern/draw_cache_extract_mesh_private.h | 509 ++ .../intern/draw_cache_extract_mesh_render_data.c | 374 ++ source/blender/editors/include/ED_uvedit.h | 1 + 7 files changed, 5707 insertions(+), 5545 deletions(-) create mode 100644 source/blender/draw/intern/draw_cache_extract_mesh_extractors.c create mode 100644 source/blender/draw/intern/draw_cache_extract_mesh_private.h create mode 100644 source/blender/draw/intern/draw_cache_extract_mesh_render_data.c diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 0541aa982f3..adbe7fdf274 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -51,6 +51,8 @@ set(INC set(SRC intern/draw_cache.c + intern/draw_cache_extract_mesh_extractors.c + intern/draw_cache_extract_mesh_render_data.c intern/draw_cache_extract_mesh.c intern/draw_cache_impl_curve.cc intern/draw_cache_impl_displist.c diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index e6b7fb9ddf5..abba3aeeb70 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -24,6 +24,10 @@ struct TaskGraph; +#include "GPU_batch.h" +#include "GPU_index_buffer.h" +#include "GPU_vertex_buffer.h" + /* Vertex Group Selection and display options */ typedef struct DRW_MeshWeightState { int defgroup_active; @@ -80,12 +84,6 @@ typedef enum eMRDataType { MR_DATA_TAN_LOOP_NOR = 1 << 4, } eMRDataType; -typedef enum eMRExtractType { - MR_EXTRACT_BMESH, - MR_EXTRACT_MAPPED, - MR_EXTRACT_MESH, -} eMRExtractType; - BLI_INLINE int mesh_render_mat_len_get(Mesh *me) { /* In edit mode, the displayed mesh is stored in the edit-mesh. */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index a2ec3e4e94e..0d2a4704b1b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -22,5536 +22,112 @@ * * \brief Extraction of Mesh data into VBO to feed to GPU. */ +#include "MEM_guardedalloc.h" -#include "MEM_guardedalloc.h" - -#include "BLI_alloca.h" -#include "BLI_bitmap.h" -#include "BLI_buffer.h" -#include "BLI_edgehash.h" -#include "BLI_jitter_2d.h" -#include "BLI_math_bits.h" -#include "BLI_math_vector.h" -#include "BLI_string.h" -#include "BLI_task.h" -#include "BLI_utildefines.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BKE_bvhutils.h" -#include "BKE_customdata.h" -#include "BKE_deform.h" -#include "BKE_editmesh.h" -#include "BKE_editmesh_bvh.h" -#include "BKE_editmesh_cache.h" -#include "BKE_editmesh_tangent.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_mesh_tangent.h" -#include "BKE_modifier.h" -#include "BKE_object_deform.h" -#include "BKE_paint.h" - -#include "atomic_ops.h" - -#include "bmesh.h" - -#include "GPU_batch.h" -#include "GPU_capabilities.h" - -#include "DRW_render.h" - -#include "ED_mesh.h" -#include "ED_uvedit.h" - -#include "draw_cache_impl.h" -#include "draw_cache_inline.h" - -#include "draw_cache_extract.h" - -// #define DEBUG_TIME - -#ifdef DEBUG_TIME -# include "PIL_time_utildefines.h" -#endif - -#define CHUNK_SIZE 8192 - -/* - * Max number of extractions types. - */ -#define M_EXTRACT_LEN 38 - -/* ---------------------------------------------------------------------- */ -/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). - * \{ */ - -typedef struct MeshRenderData { - eMRExtractType extract_type; - - int poly_len, edge_len, vert_len, loop_len; - int edge_loose_len; - int vert_loose_len; - int loop_loose_len; - int tri_len; - int mat_len; - - bool use_hide; - bool use_subsurf_fdots; - bool use_final_mesh; - - /** Use for #MeshStatVis calculation which use world-space coords. */ - float obmat[4][4]; - - const ToolSettings *toolsettings; - /** Edit Mesh */ - BMEditMesh *edit_bmesh; - BMesh *bm; - EditMeshData *edit_data; - - /* For deformed edit-mesh data. */ - /* Use for #ME_WRAPPER_TYPE_BMESH. */ - const float (*bm_vert_coords)[3]; - const float (*bm_vert_normals)[3]; - const float (*bm_poly_normals)[3]; - const float (*bm_poly_centers)[3]; - - int *v_origindex, *e_origindex, *p_origindex; - int crease_ofs; - int bweight_ofs; - int freestyle_edge_ofs; - int freestyle_face_ofs; - /** Mesh */ - Mesh *me; - const MVert *mvert; - const MEdge *medge; - const MLoop *mloop; - const MPoly *mpoly; - BMVert *eve_act; - BMEdge *eed_act; - BMFace *efa_act; - BMFace *efa_act_uv; - /* Data created on-demand (usually not for #BMesh based data). */ - MLoopTri *mlooptri; - float (*loop_normals)[3]; - float (*poly_normals)[3]; - int *lverts, *ledges; -} MeshRenderData; - -static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) -{ - mr->ledges = cache->ledges; - mr->lverts = cache->lverts; - mr->vert_loose_len = cache->vert_loose_len; - mr->edge_loose_len = cache->edge_loose_len; - - mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); -} - -static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, - MeshBufferExtractionCache *cache) -{ - /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges - * and verts are calculated at the same time.*/ - if (cache->lverts) { - return; - } - - cache->vert_loose_len = 0; - cache->edge_loose_len = 0; - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - - BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); - - cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); - const MEdge *med = mr->medge; - for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { - if (med->flag & ME_LOOSEEDGE) { - cache->ledges[cache->edge_loose_len++] = med_index; - } - /* Tag verts as not loose. */ - BLI_BITMAP_ENABLE(lvert_map, med->v1); - BLI_BITMAP_ENABLE(lvert_map, med->v2); - } - if (cache->edge_loose_len < mr->edge_len) { - cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); - } - - cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); - for (int v = 0; v < mr->vert_len; v++) { - if (!BLI_BITMAP_TEST(lvert_map, v)) { - cache->lverts[cache->vert_loose_len++] = v; - } - } - if (cache->vert_loose_len < mr->vert_len) { - cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); - } - - MEM_freeN(lvert_map); - } - else { - /* #BMesh */ - BMesh *bm = mr->bm; - int elem_id; - BMIter iter; - BMVert *eve; - BMEdge *ede; - - cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__); - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { - if (eve->e == NULL) { - cache->lverts[cache->vert_loose_len++] = elem_id; - } - } - if (cache->vert_loose_len < mr->vert_len) { - cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); - } - - cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); - BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { - if (ede->l == NULL) { - cache->ledges[cache->edge_loose_len++] = elem_id; - } - } - if (cache->edge_loose_len < mr->edge_len) { - cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); - } - } -} - -/** - * Part of the creation of the #MeshRenderData that happens in a thread. - */ -static void mesh_render_data_update_looptris(MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - Mesh *me = mr->me; - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); - BKE_mesh_recalc_looptri( - me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); - } - } - else { - /* #BMesh */ - if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - /* Edit mode ensures this is valid, no need to calculate. */ - BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); - } - } -} - -static void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRIterType UNUSED(iter_type), - const eMRDataType data_flag) -{ - Mesh *me = mr->me; - const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; - const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { - mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); - BKE_mesh_calc_normals_poly((MVert *)mr->mvert, - NULL, - mr->vert_len, - mr->mloop, - mr->mpoly, - mr->loop_len, - mr->poly_len, - mr->poly_normals, - true); - } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); - short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL); - BKE_mesh_normals_loop_split(mr->me->mvert, - mr->vert_len, - mr->me->medge, - mr->edge_len, - mr->me->mloop, - mr->loop_normals, - mr->loop_len, - mr->me->mpoly, - mr->poly_normals, - mr->poly_len, - is_auto_smooth, - split_angle, - NULL, - clnors, - NULL); - } - } - else { - /* #BMesh */ - if (data_flag & MR_DATA_POLY_NOR) { - /* Use #BMFace.no instead. */ - } - if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { - - const float(*vert_coords)[3] = NULL; - const float(*vert_normals)[3] = NULL; - const float(*poly_normals)[3] = NULL; - - if (mr->edit_data && mr->edit_data->vertexCos) { - vert_coords = mr->bm_vert_coords; - vert_normals = mr->bm_vert_normals; - poly_normals = mr->bm_poly_normals; - } - - mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); - const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); - BM_loops_calc_normal_vcos(mr->bm, - vert_coords, - vert_normals, - poly_normals, - is_auto_smooth, - split_angle, - mr->loop_normals, - NULL, - NULL, - clnors_offset, - false); - } - } -} - -/** - * \param is_mode_active: When true, use the modifiers from the edit-data, - * otherwise don't use modifiers as they are not from this object. - */ -static MeshRenderData *mesh_render_data_create(Mesh *me, - MeshBufferExtractionCache *cache, - const bool is_editmode, - const bool is_paint_mode, - const bool is_mode_active, - const float obmat[4][4], - const bool do_final, - const bool do_uvedit, - const DRW_MeshCDMask *UNUSED(cd_used), - const ToolSettings *ts, - const eMRIterType iter_type) -{ - MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); - mr->toolsettings = ts; - mr->mat_len = mesh_render_mat_len_get(me); - - copy_m4_m4(mr->obmat, obmat); - - if (is_editmode) { - BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); - mr->bm = me->edit_mesh->bm; - mr->edit_bmesh = me->edit_mesh; - mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; - mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; - - if (mr->edit_data) { - EditMeshData *emd = mr->edit_data; - if (emd->vertexCos) { - BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd); - BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd); - } - - mr->bm_vert_coords = mr->edit_data->vertexCos; - mr->bm_vert_normals = mr->edit_data->vertexNos; - mr->bm_poly_normals = mr->edit_data->polyNos; - mr->bm_poly_centers = mr->edit_data->polyCos; - } - - bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); - bool use_mapped = is_mode_active && - (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); - - int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; - - BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); - BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); - - mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); - mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); - mr->eed_act = BM_mesh_active_edge_get(mr->bm); - mr->eve_act = BM_mesh_active_vert_get(mr->bm); - - mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE); - mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT); -#ifdef WITH_FREESTYLE - mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); - mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); -#endif - - if (use_mapped) { - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - - use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); - } - - mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH; - - /* Seems like the mesh_eval_final do not have the right origin indices. - * Force not mapped in this case. */ - if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { - // mr->edit_bmesh = NULL; - mr->extract_type = MR_EXTRACT_MESH; - } - } - else { - mr->me = me; - mr->edit_bmesh = NULL; - - bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original; - if (use_mapped) { - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - - use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); - } - - mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH; - } - - if (mr->extract_type != MR_EXTRACT_BMESH) { - /* Mesh */ - mr->vert_len = mr->me->totvert; - mr->edge_len = mr->me->totedge; - mr->loop_len = mr->me->totloop; - mr->poly_len = mr->me->totpoly; - mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); - - mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); - mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); - mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); - mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); - - mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); - mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); - mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); - } - else { - /* #BMesh */ - BMesh *bm = mr->bm; - - mr->vert_len = bm->totvert; - mr->edge_len = bm->totedge; - mr->loop_len = bm->totloop; - mr->poly_len = bm->totface; - mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); - } - - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - mesh_render_data_loose_geom_ensure(mr, cache); - mesh_render_data_loose_geom_load(mr, cache); - } - - return mr; -} - -static void mesh_render_data_free(MeshRenderData *mr) -{ - MEM_SAFE_FREE(mr->mlooptri); - MEM_SAFE_FREE(mr->poly_normals); - MEM_SAFE_FREE(mr->loop_normals); - - /* Loose geometry are owned by MeshBufferExtractionCache. */ - mr->ledges = NULL; - mr->lverts = NULL; - - MEM_freeN(mr); -} - -BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) -{ - return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_face_at_index(mr->bm, mr->p_origindex[idx]) : - NULL; -} - -BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx) -{ - return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_edge_at_index(mr->bm, mr->e_origindex[idx]) : - NULL; -} - -BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx) -{ - return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? - BM_vert_at_index(mr->bm, mr->v_origindex[idx]) : - NULL; -} - -BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve) -{ - const float(*vert_coords)[3] = mr->bm_vert_coords; - if (vert_coords != NULL) { - return vert_coords[BM_elem_index_get(eve)]; - } - - UNUSED_VARS(mr); - return eve->co; -} - -BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve) -{ - const float(*vert_normals)[3] = mr->bm_vert_normals; - if (vert_normals != NULL) { - return vert_normals[BM_elem_index_get(eve)]; - } - - UNUSED_VARS(mr); - return eve->no; -} - -BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa) -{ - const float(*poly_normals)[3] = mr->bm_poly_normals; - if (poly_normals != NULL) { - return poly_normals[BM_elem_index_get(efa)]; - } - - UNUSED_VARS(mr); - return efa->no; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loop Triangles - * \{ */ - -typedef struct ExtractTriBMesh_Params { - BMLoop *(*looptris)[3]; - int tri_range[2]; -} ExtractTriBMesh_Params; -typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, - BMLoop **elt, - const int elt_index, - void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriBMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 3) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END } - -typedef struct ExtractTriMesh_Params { - const MLoopTri *mlooptri; - int tri_range[2]; -} ExtractTriMesh_Params; -typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, - const MLoopTri *mlt, - const int elt_index, - void *data); - -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ - CHECK_TYPE(params, const ExtractTriMesh_Params *); \ - { \ - const int _tri_index_end = (params)->tri_range[1]; \ - const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \ - for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ - index_tri += 1, elem_tri += 1) -#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Polygons, Loops - * \{ */ - -typedef struct ExtractPolyBMesh_Params { - BMLoop *(*looptris)[3]; - int poly_range[2]; -} ExtractPolyBMesh_Params; -typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, - BMFace *f, - const int f_index, - void *data); - -#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_poly = _ftable[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_BM_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMFace **_ftable = mr->bm->ftable; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - BMFace *elem_face = _ftable[index_poly]; \ - BMLoop *elem_loop, *l_first; \ - elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \ - do { \ - const int index_loop = BM_elem_index_get(elem_loop); \ - (void)index_loop; /* Quiet warning when unused. */ - -#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \ - } \ - while ((elem_loop = elem_loop->next) != l_first) \ - ; \ - } \ - } - -typedef struct ExtractPolyMesh_Params { - int poly_range[2]; -} ExtractPolyMesh_Params; -typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data); - -#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - (void)elem_poly; - -#define EXTRACT_POLY_FOREACH_MESH_END \ - } \ - } - -/* Iterate over polygon and loop. */ -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \ - elem_poly, index_poly, elem_loop, index_loop, params, mr) \ - CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ - { \ - const MPoly *_mpoly = mr->mpoly; \ - const MLoop *_mloop = mr->mloop; \ - const int _poly_index_end = (params)->poly_range[1]; \ - for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ - index_poly += 1) { \ - const MPoly *elem_poly = &_mpoly[index_poly]; \ - const int _index_end = elem_poly->loopstart + elem_poly->totloop; \ - for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \ - const MLoop *elem_loop = &_mloop[index_loop]; \ - (void)elem_loop; - -#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Edges - * \{ */ - -typedef struct ExtractLEdgeBMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeBMesh_Params; -typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *data); - -#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ - CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \ - BMEdge **_etable = mr->bm->etable; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLEdgeMesh_Params { - const int *ledge; - int ledge_range[2]; -} ExtractLEdgeMesh_Params; -typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *data); - -#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ - CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \ - { \ - const MEdge *_medge = mr->medge; \ - const int *_ledge = (params)->ledge; \ - const int _ledge_index_end = (params)->ledge_range[1]; \ - for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ - index_ledge += 1) { \ - const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \ - (void)elem_edge; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LEDGE_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract: Loose Vertices - * \{ */ - -typedef struct ExtractLVertBMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertBMesh_Params; -typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *data); - -#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ - CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \ - { \ - BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ - BMVert **vtable = mr->bm->vtable; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - BMVert *elem_vert = vtable[lverts[index_lvert]]; \ - (void)elem_vert; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_BM_END \ - } \ - } \ - } - -typedef struct ExtractLVertMesh_Params { - const int *lvert; - int lvert_range[2]; -} ExtractLVertMesh_Params; -typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, - const MVert *mv, - const int lvert_index, - void *data); - -#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ - CHECK_TYPE(params, const ExtractLVertMesh_Params *); \ - { \ - const MVert *mvert = mr->mvert; \ - const int *lverts = (params)->lvert; \ - const int _lvert_index_end = (params)->lvert_range[1]; \ - for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ - index_lvert += 1) { \ - const MVert *elem = &mvert[lverts[index_lvert]]; \ - (void)elem; /* Quiet warning when unused. */ \ - { -#define EXTRACT_LVERT_FOREACH_MESH_END \ - } \ - } \ - } - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract Struct - * \{ */ - -typedef void *(ExtractInitFn)(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buffer); -typedef void(ExtractFinishFn)(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buffer, - void *data); - -typedef struct MeshExtract { - /** Executed on main thread and return user data for iteration functions. */ - ExtractInitFn *init; - /** Executed on one (or more if use_threading) worker thread(s). */ - ExtractTriBMeshFn *iter_looptri_bm; - ExtractTriMeshFn *iter_looptri_mesh; - ExtractPolyBMeshFn *iter_poly_bm; - ExtractPolyMeshFn *iter_poly_mesh; - ExtractLEdgeBMeshFn *iter_ledge_bm; - ExtractLEdgeMeshFn *iter_ledge_mesh; - ExtractLVertBMeshFn *iter_lvert_bm; - ExtractLVertMeshFn *iter_lvert_mesh; - /** Executed on one worker thread after all elements iterations. */ - ExtractFinishFn *finish; - /** Used to request common data. */ - const eMRDataType data_flag; - /** Used to know if the element callbacks are thread-safe and can be parallelized. */ - const bool use_threading; - /** - * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index - * buffer. - */ - const size_t mesh_buffer_offset; -} MeshExtract; - -static void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) -{ - /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to - * `MeshBufferCache *`. What shows a different usage versus intent. */ - void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset); - void *buffer = *buffer_ptr; - BLI_assert(buffer); - return buffer; -} - -typedef struct MeshExtractRunData { - const MeshExtract *extractor; - void *buffer; - void *user_data; -} MeshExtractRunData; - -typedef struct MeshExtractRunDataArray { - int len; - MeshExtractRunData items[M_EXTRACT_LEN]; -} MeshExtractRunDataArray; - -static void mesh_extract_run_data_array_init(MeshExtractRunDataArray *array) -{ - array->len = 0; -} - -static void mesh_extract_run_data_array_add_ex(MeshExtractRunDataArray *array, - const MeshExtractRunData *run_data) -{ - array->items[array->len] = *run_data; - array->len++; -} - -static void mesh_extract_run_data_array_add(MeshExtractRunDataArray *array, - const MeshExtract *extractor) -{ - MeshExtractRunData run_data; - run_data.extractor = extractor; - run_data.buffer = NULL; - run_data.user_data = NULL; - mesh_extract_run_data_array_add_ex(array, &run_data); -} - -static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray *src, - MeshExtractRunDataArray *dst, - eMRIterType iter_type) -{ - for (int i = 0; i < src->len; i++) { - - const MeshExtractRunData *data = &src->items[i]; - const MeshExtract *extractor = data->extractor; - if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { - BLI_assert(extractor->iter_looptri_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { - BLI_assert(extractor->iter_poly_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { - BLI_assert(extractor->iter_ledge_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { - BLI_assert(extractor->iter_lvert_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - } -} - -static void mesh_extract_run_data_array_filter_threading( - const MeshExtractRunDataArray *src, MeshExtractRunDataArray *dst_multi_threaded) -{ - for (int i = 0; i < src->len; i++) { - const MeshExtract *extractor = src->items[i].extractor; - if (extractor->use_threading) { - mesh_extract_run_data_array_add(dst_multi_threaded, extractor); - } - } -} - -BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext) -{ - eMRIterType type = 0; - SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI); - SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY); - SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE); - SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT); - return type; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Triangles Indices - * \{ */ - -typedef struct MeshExtract_Tri_Data { - GPUIndexBufBuilder elb; - int *tri_mat_start; - int *tri_mat_end; -} MeshExtract_Tri_Data; - -static void *extract_tris_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__); - - size_t mat_tri_idx_size = sizeof(int) * mr->mat_len; - data->tri_mat_start = MEM_callocN(mat_tri_idx_size, __func__); - data->tri_mat_end = MEM_callocN(mat_tri_idx_size, __func__); - - int *mat_tri_len = data->tri_mat_start; - /* Count how many triangle for each material. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMFace *efa; - BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - int mat = min_ii(efa->mat_nr, mr->mat_len - 1); - mat_tri_len[mat] += efa->len - 2; - } - } - } - else { - const MPoly *mp = mr->mpoly; - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int mat = min_ii(mp->mat_nr, mr->mat_len - 1); - mat_tri_len[mat] += mp->totloop - 2; - } - } - } - /* Accumulate triangle lengths per material to have correct offsets. */ - int ofs = mat_tri_len[0]; - mat_tri_len[0] = 0; - for (int i = 1; i < mr->mat_len; i++) { - int tmp = mat_tri_len[i]; - mat_tri_len[i] = ofs; - ofs += tmp; - } - - memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size); - - int visible_tri_tot = ofs; - GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len); - - return data; -} - -static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, - BMLoop **elt, - const int UNUSED(elt_index), - void *_data) -{ - MeshExtract_Tri_Data *data = _data; - const int mat_last = mr->mat_len - 1; - - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(elt[0]->f->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts(&data->elb, - mat_tri_ofs[mat]++, - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); - } -} - -static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, - const MLoopTri *mlt, - const int UNUSED(elt_index), - void *_data) -{ - MeshExtract_Tri_Data *data = _data; - const int mat_last = mr->mat_len - 1; - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(mp->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts( - &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); - } -} - -static void extract_tris_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_Tri_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); - - /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch - * is created before the surfaces-per-material. */ - if (mr->use_final_mesh && cache->final.tris_per_mat) { - MeshBufferCache *mbc_final = &cache->final; - for (int i = 0; i < mr->mat_len; i++) { - /* These IBOs have not been queried yet but we create them just in case they are needed - * later since they are not tracked by mesh_buffer_cache_create_requested(). */ - if (mbc_final->tris_per_mat[i] == NULL) { - mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); - } - /* Multiply by 3 because these are triangle indices. */ - const int mat_start = data->tri_mat_start[i]; - const int mat_end = data->tri_mat_end[i]; - const int start = mat_start * 3; - const int len = (mat_end - mat_start) * 3; - GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); - } - } - MEM_freeN(data->tri_mat_start); - MEM_freeN(data->tri_mat_end); - MEM_freeN(data); -} - -static const MeshExtract extract_tris = { - .init = extract_tris_init, - .iter_looptri_bm = extract_tris_iter_looptri_bm, - .iter_looptri_mesh = extract_tris_iter_looptri_mesh, - .finish = extract_tris_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edges Indices - * \{ */ - -static void *extract_lines_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); - /* Put loose edges at the end. */ - GPU_indexbuf_init( - elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len); - return elb; -} - -static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *elb) -{ - BMLoop *l_iter, *l_first; - /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ - l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; - do { - if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_line_verts(elb, - BM_elem_index_get(l_iter->e), - BM_elem_index_get(l_iter), - BM_elem_index_get(l_iter->next)); - } - else { - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); - } - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *elb) -{ - /* Using poly & loop iterator would complicate accessing the adjacent loop. */ - const MLoop *mloop = mr->mloop; - const MEdge *medge = mr->medge; - if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - const MEdge *med = &medge[ml->e]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } - else { - GPU_indexbuf_set_line_restart(elb, ml->e); - } - } while ((ml_index = ml_index_next++) != ml_index_last); - } - else { - const int ml_index_last = mp->loopstart + (mp->totloop - 1); - int ml_index = ml_index_last, ml_index_next = mp->loopstart; - do { - const MLoop *ml = &mloop[ml_index]; - GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); - } while ((ml_index = ml_index_next++) != ml_index_last); - } -} - -static void extract_lines_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *elb) -{ - const int l_index_offset = mr->edge_len + ledge_index; - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); -} - -static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *elb) -{ - const int l_index_offset = mr->edge_len + ledge_index; - const int e_index = mr->ledges[ledge_index]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { - const int l_index = mr->loop_len + ledge_index * 2; - GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); - } - else { - GPU_indexbuf_set_line_restart(elb, l_index_offset); - } - /* Don't render the edge twice. */ - GPU_indexbuf_set_line_restart(elb, e_index); -} - -static void extract_lines_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *elb) -{ - GPUIndexBuf *ibo = buf; - GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); -} - -static const MeshExtract extract_lines = { - .init = extract_lines_init, - .iter_poly_bm = extract_lines_iter_poly_bm, - .iter_poly_mesh = extract_lines_iter_poly_mesh, - .iter_ledge_bm = extract_lines_iter_ledge_bm, - .iter_ledge_mesh = extract_lines_iter_ledge_mesh, - .finish = extract_lines_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Loose Edges Sub Buffer - * \{ */ - -static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache) -{ - BLI_assert(cache->final.ibo.lines); - /* Multiply by 2 because these are edges indices. */ - const int start = mr->edge_len * 2; - const int len = mr->edge_loose_len * 2; - GPU_indexbuf_create_subrange_in_place( - cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len); - cache->no_loose_wire = (len == 0); -} - -static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *elb) -{ - GPUIndexBuf *ibo = buf; - GPU_indexbuf_build_in_place(elb, ibo); - extract_lines_loose_subbuffer(mr, cache); - MEM_freeN(elb); -} - -static const MeshExtract extract_lines_with_lines_loose = { - .init = extract_lines_init, - .iter_poly_bm = extract_lines_iter_poly_bm, - .iter_poly_mesh = extract_lines_iter_poly_mesh, - .iter_ledge_bm = extract_lines_iter_ledge_bm, - .iter_ledge_mesh = extract_lines_iter_ledge_mesh, - .finish = extract_lines_with_lines_loose_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Point Indices - * \{ */ - -static void *extract_points_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); - GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len); - return elb; -} - -BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, BMVert *eve, int l_index) -{ - const int v_index = BM_elem_index_get(eve); - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_point_vert(elb, v_index, l_index); - } - else { - GPU_indexbuf_set_point_restart(elb, v_index); - } -} - -BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, - const MeshRenderData *mr, - const int v_index, - const int l_index) -{ - const MVert *mv = &mr->mvert[v_index]; - if (!((mr->use_hide && (mv->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && - (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) { - GPU_indexbuf_set_point_vert(elb, v_index, l_index); - } - else { - GPU_indexbuf_set_point_restart(elb, v_index); - } -} - -static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *elb) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - vert_set_bm(elb, l_iter->v, l_index); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_points_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *elb) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - vert_set_mesh(elb, mr, ml->v, ml_index); - } -} - -static void extract_points_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *elb) -{ - vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); - vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); -} - -static void extract_points_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *elb) -{ - vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); - vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); -} - -static void extract_points_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *elb) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - vert_set_bm(elb, eve, offset + lvert_index); -} - -static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *UNUSED(mv), - const int lvert_index, - void *elb) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); -} - -static void extract_points_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *elb) -{ - GPUIndexBuf *ibo = buf; - GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); -} - -static const MeshExtract extract_points = { - .init = extract_points_init, - .iter_poly_bm = extract_points_iter_poly_bm, - .iter_poly_mesh = extract_points_iter_poly_mesh, - .iter_ledge_bm = extract_points_iter_ledge_bm, - .iter_ledge_mesh = extract_points_iter_ledge_mesh, - .iter_lvert_bm = extract_points_iter_lvert_bm, - .iter_lvert_mesh = extract_points_iter_lvert_mesh, - .finish = extract_points_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Indices - * \{ */ - -static void *extract_fdots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); - GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); - return elb; -} - -static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int f_index, - void *elb) -{ - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - GPU_indexbuf_set_point_vert(elb, f_index, f_index); - } - else { - GPU_indexbuf_set_point_restart(elb, f_index); - } -} - -static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *elb) -{ - if (mr->use_subsurf_fdots) { - /* Check #ME_VERT_FACEDOT. */ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - const MVert *mv = &mr->mvert[ml->v]; - if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - return; - } - } - GPU_indexbuf_set_point_restart(elb, mp_index); - } - else { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); - } - else { - GPU_indexbuf_set_point_restart(elb, mp_index); - } - } -} - -static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *elb) -{ - GPUIndexBuf *ibo = buf; - GPU_indexbuf_build_in_place(elb, ibo); - MEM_freeN(elb); -} - -static const MeshExtract extract_fdots = { - .init = extract_fdots_init, - .iter_poly_bm = extract_fdots_iter_poly_bm, - .iter_poly_mesh = extract_fdots_iter_poly_mesh, - .finish = extract_fdots_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Paint Mask Line Indices - * \{ */ - -typedef struct MeshExtract_LinePaintMask_Data { - GPUIndexBufBuilder elb; - /** One bit per edge set if face is selected. */ - BLI_bitmap select_map[0]; -} MeshExtract_LinePaintMask_Data; - -static void *extract_lines_paint_mask_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); - MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len); - return data; -} - -static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_LinePaintMask_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const int e_index = ml->e; - const MEdge *me = &mr->medge[e_index]; - if (!((mr->use_hide && (me->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && - (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { - - const int ml_index_last = mp->totloop + mp->loopstart - 1; - const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); - if (mp->flag & ME_FACE_SEL) { - if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) { - /* Hide edge as it has more than 2 selected loop. */ - GPU_indexbuf_set_line_restart(&data->elb, e_index); - } - else { - /* First selected loop. Set edge visible, overwriting any unselected loop. */ - GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); - } - } - else { - /* Set these unselected loop only if this edge has no other selected loop. */ - if (!BLI_BITMAP_TEST(data->select_map, e_index)) { - GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); - } - } - } - else { - GPU_indexbuf_set_line_restart(&data->elb, e_index); - } - } -} - -static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_LinePaintMask_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); -} - -static const MeshExtract extract_lines_paint_mask = { - .init = extract_lines_paint_mask_init, - .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh, - .finish = extract_lines_paint_mask_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Line Adjacency Indices - * \{ */ - -#define NO_EDGE INT_MAX - -typedef struct MeshExtract_LineAdjacency_Data { - GPUIndexBufBuilder elb; - EdgeHash *eh; - bool is_manifold; - /* Array to convert vert index to any loop index of this vert. */ - uint vert_to_loop[0]; -} MeshExtract_LineAdjacency_Data; - -static void *extract_lines_adjacency_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf)) -{ - /* Similar to poly_to_tri_count(). - * There is always (loop + triangle - 1) edges inside a polygon. - * Accumulate for all polys and you get : */ - uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len; - - size_t vert_to_loop_size = sizeof(uint) * mr->vert_len; - - MeshExtract_LineAdjacency_Data *data = MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len); - data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len); - data->is_manifold = true; - return data; -} - -BLI_INLINE void lines_adjacency_triangle( - uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data) -{ - GPUIndexBufBuilder *elb = &data->elb; - /* Iterate around the triangle's edges. */ - for (int e = 0; e < 3; e++) { - SHIFT3(uint, v3, v2, v1); - SHIFT3(uint, l3, l2, l1); - - bool inv_indices = (v2 > v3); - void **pval; - bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval); - int v_data = POINTER_AS_INT(*pval); - if (!value_is_init || v_data == NO_EDGE) { - /* Save the winding order inside the sign bit. Because the - * Edge-hash sort the keys and we need to compare winding later. */ - int value = (int)l1 + 1; /* 0 cannot be signed so add one. */ - *pval = POINTER_FROM_INT((inv_indices) ? -value : value); - /* Store loop indices for remaining non-manifold edges. */ - data->vert_to_loop[v2] = l2; - data->vert_to_loop[v3] = l3; - } - else { - /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */ - *pval = POINTER_FROM_INT(NO_EDGE); - bool inv_opposite = (v_data < 0); - uint l_opposite = (uint)abs(v_data) - 1; - /* TODO Make this part thread-safe. */ - if (inv_opposite == inv_indices) { - /* Don't share edge if triangles have non matching winding. */ - GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1); - GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite); - data->is_manifold = false; - } - else { - GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite); - } - } - } -} - -static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - BMLoop **elt, - const int UNUSED(elt_index), - void *data) -{ - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), - BM_elem_index_get(elt[1]->v), - BM_elem_index_get(elt[2]->v), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2]), - data); - } -} - -static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, - const MLoopTri *mlt, - const int UNUSED(elt_index), - void *data) -{ - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, - mr->mloop[mlt->tri[1]].v, - mr->mloop[mlt->tri[2]].v, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2], - data); - } -} - -static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *cache, - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_LineAdjacency_Data *data = _data; - /* Create edges for remaining non manifold edges. */ - EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); - for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { - uint v2, v3, l1, l2, l3; - int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi)); - if (v_data != NO_EDGE) { - BLI_edgehashIterator_getKey(ehi, &v2, &v3); - l1 = (uint)abs(v_data) - 1; - if (v_data < 0) { /* inv_opposite */ - SWAP(uint, v2, v3); - } - l2 = data->vert_to_loop[v2]; - l3 = data->vert_to_loop[v3]; - GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1); - data->is_manifold = false; - } - } - BLI_edgehashIterator_free(ehi); - BLI_edgehash_free(data->eh, NULL); - - cache->is_manifold = data->is_manifold; - - GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); -} - -#undef NO_EDGE - -static const MeshExtract extract_lines_adjacency = { - .init = extract_lines_adjacency_init, - .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm, - .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh, - .finish = extract_lines_adjacency_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Triangles Indices - * \{ */ - -typedef struct MeshExtract_EditUvElem_Data { - GPUIndexBufBuilder elb; - bool sync_selection; -} MeshExtract_EditUvElem_Data; - -static void *extract_edituv_tris_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_tri_add( - MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3); - } -} - -static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), - BMLoop **elt, - const int UNUSED(elt_index), - void *data) -{ - edituv_tri_add(data, - BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), - BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), - BM_elem_index_get(elt[0]), - BM_elem_index_get(elt[1]), - BM_elem_index_get(elt[2])); -} - -static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, - const MLoopTri *mlt, - const int UNUSED(elt_index), - void *data) -{ - const MPoly *mp = &mr->mpoly[mlt->poly]; - edituv_tri_add(data, - (mp->flag & ME_HIDE) != 0, - (mp->flag & ME_FACE_SEL) != 0, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2]); -} - -static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *extract_data = data; - GPU_indexbuf_build_in_place(&extract_data->elb, ibo); - MEM_freeN(extract_data); -} - -static const MeshExtract extract_edituv_tris = { - .init = extract_edituv_tris_init, - .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm, - .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh, - .finish = extract_edituv_tris_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Line Indices around faces - * \{ */ - -static void *extract_edituv_lines_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); - - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_edge_add( - MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_add_line_verts(&data->elb, v1, v2); - } -} - -static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - edituv_edge_add(data, - BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), - BM_elem_flag_test_bool(f, BM_ELEM_SELECT), - l_index, - BM_elem_index_get(l_iter->next)); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const int ml_index_last = mp->totloop + mp->loopstart - 1; - const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); - const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE); - edituv_edge_add(data, - (mp->flag & ME_HIDE) != 0 || !real_edge, - (mp->flag & ME_FACE_SEL) != 0, - ml_index, - ml_index_next); - } -} - -static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *extract_data = data; - GPU_indexbuf_build_in_place(&extract_data->elb, ibo); - MEM_freeN(extract_data); -} - -static const MeshExtract extract_edituv_lines = { - .init = extract_edituv_lines_init, - .iter_poly_bm = extract_edituv_lines_iter_poly_bm, - .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh, - .finish = extract_edituv_lines_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Points Indices - * \{ */ - -static void *extract_edituv_points_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); - - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, - bool hidden, - bool selected, - int v1) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_add_point_vert(&data->elb, v1); - } -} - -static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - edituv_point_add( - data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && - mr->v_origindex[ml->v] != ORIGINDEX_NONE); - edituv_point_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); - } -} - -static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *extract_data = data; - GPU_indexbuf_build_in_place(&extract_data->elb, ibo); - MEM_freeN(extract_data); -} - -static const MeshExtract extract_edituv_points = { - .init = extract_edituv_points_init, - .iter_poly_bm = extract_edituv_points_iter_poly_bm, - .iter_poly_mesh = extract_edituv_points_iter_poly_mesh, - .finish = extract_edituv_points_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Facedots Indices - * \{ */ - -static void *extract_edituv_fdots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(ibo)) -{ - MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); - GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); - - data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; - return data; -} - -BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, - bool hidden, - bool selected, - int face_index) -{ - if (!hidden && (data->sync_selection || selected)) { - GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index); - } - else { - GPU_indexbuf_set_point_restart(&data->elb, face_index); - } -} - -static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int f_index, - void *data) -{ - edituv_facedot_add(data, - BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), - BM_elem_flag_test_bool(f, BM_ELEM_SELECT), - f_index); -} - -static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - if (mr->use_subsurf_fdots) { - /* Check #ME_VERT_FACEDOT. */ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - const bool subd_fdot = (!mr->use_subsurf_fdots || - (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0); - edituv_facedot_add(data, - ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, - (mp->flag & ME_FACE_SEL) != 0, - mp_index); - } - } - else { - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); - edituv_facedot_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); - } -} - -static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *_data) -{ - GPUIndexBuf *ibo = buf; - MeshExtract_EditUvElem_Data *data = _data; - GPU_indexbuf_build_in_place(&data->elb, ibo); - MEM_freeN(data); -} - -static const MeshExtract extract_edituv_fdots = { - .init = extract_edituv_fdots_init, - .iter_poly_bm = extract_edituv_fdots_iter_poly_bm, - .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh, - .finish = extract_edituv_fdots_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Position and Vertex Normal - * \{ */ - -typedef struct PosNorLoop { - float pos[3]; - GPUPackedNormal nor; -} PosNorLoop; - -typedef struct MeshExtract_PosNor_Data { - PosNorLoop *vbo_data; - GPUNormal normals[]; -} MeshExtract_PosNor_Data; - -static void *extract_pos_nor_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* WARNING Adjust #PosNorLoop struct accordingly. */ - GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_alias_add(&format, "vnor"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - - /* Pack normals per vert, reduce amount of computation. */ - size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; - MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); - data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo); - - /* Quicker than doing it for each loop. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMVert *eve; - int v; - BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) { - data->normals[v].low = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve)); - } - } - else { - const MVert *mv = mr->mvert; - for (int v = 0; v < mr->vert_len; v++, mv++) { - data->normals[v].low = GPU_normal_convert_i10_s3(mv->no); - } - } - return data; -} - -static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); - vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low; - vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - PosNorLoop *vert = &data->vbo_data[ml_index]; - const MVert *mv = &mr->mvert[ml->v]; - copy_v3_v3(vert->pos, mv->co); - vert->nor = data->normals[ml->v].low; - /* Flag for paint mode overlay. */ - if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && - (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { - vert->nor.w = -1; - } - else if (mv->flag & SELECT) { - vert->nor.w = 1; - } - else { - vert->nor.w = 0; - } - } -} - -static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - - int l_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; - vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; -} - -static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - vert[0].nor = data->normals[med->v1].low; - vert[1].nor = data->normals[med->v2].low; -} - -static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int l_index = offset + lvert_index; - PosNorLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - vert->nor = data->normals[BM_elem_index_get(eve)].low; -} - -static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *mv, - const int lvert_index, - void *_data) -{ - MeshExtract_PosNor_Data *data = _data; - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - vert->nor = data->normals[v_index].low; -} - -static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_pos_nor = { - .init = extract_pos_nor_init, - .iter_poly_bm = extract_pos_nor_iter_poly_bm, - .iter_poly_mesh = extract_pos_nor_iter_poly_mesh, - .iter_ledge_bm = extract_pos_nor_iter_ledge_bm, - .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh, - .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, - .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, - .finish = extract_pos_nor_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Position and High Quality Vertex Normal - * \{ */ - -typedef struct PosNorHQLoop { - float pos[3]; - short nor[4]; -} PosNorHQLoop; - -typedef struct MeshExtract_PosNorHQ_Data { - PosNorHQLoop *vbo_data; - GPUNormal normals[]; -} MeshExtract_PosNorHQ_Data; - -static void *extract_pos_nor_hq_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* WARNING Adjust #PosNorHQLoop struct accordingly. */ - GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_alias_add(&format, "vnor"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - - /* Pack normals per vert, reduce amount of computation. */ - size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; - MeshExtract_PosNorHQ_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); - data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo); - - /* Quicker than doing it for each loop. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMVert *eve; - int v; - BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) { - normal_float_to_short_v3(data->normals[v].high, bm_vert_no_get(mr, eve)); - } - } - else { - const MVert *mv = mr->mvert; - for (int v = 0; v < mr->vert_len; v++, mv++) { - copy_v3_v3_short(data->normals[v].high, mv->no); - } - } - return data; -} - -static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high); - - BMFace *efa = l_iter->f; - vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - const MVert *mv = &mr->mvert[ml->v]; - copy_v3_v3(vert->pos, mv->co); - copy_v3_v3_short(vert->nor, data->normals[ml->v].high); - - /* Flag for paint mode overlay. */ - if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && - (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { - vert->nor[3] = -1; - } - else if (mv->flag & SELECT) { - vert->nor[3] = 1; - } - else { - vert->nor[3] = 0; - } - } -} - -static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - int l_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); - copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); - copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); - vert[1].nor[3] = 0; -} - -static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - const int ml_index = mr->loop_len + ledge_index * 2; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); - copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); - copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); - vert[0].nor[3] = 0; - copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); - vert[1].nor[3] = 0; -} - -static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int l_index = offset + lvert_index; - PosNorHQLoop *vert = &data->vbo_data[l_index]; - copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); - copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); - vert->nor[3] = 0; -} - -static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *mv, - const int lvert_index, - void *_data) -{ - MeshExtract_PosNorHQ_Data *data = _data; - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int ml_index = offset + lvert_index; - const int v_index = mr->lverts[lvert_index]; - PosNorHQLoop *vert = &data->vbo_data[ml_index]; - copy_v3_v3(vert->pos, mv->co); - copy_v3_v3_short(vert->nor, data->normals[v_index].high); - vert->nor[3] = 0; -} - -static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_pos_nor_hq = { - .init = extract_pos_nor_hq_init, - .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm, - .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh, - .iter_ledge_bm = extract_pos_nor_hq_iter_ledge_bm, - .iter_ledge_mesh = extract_pos_nor_hq_iter_ledge_mesh, - .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm, - .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh, - .finish = extract_pos_nor_hq_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; - -/** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract HQ Loop Normal - * \{ */ - -typedef struct gpuHQNor { - short x, y, z, w; -} gpuHQNor; - -static void *extract_lnor_hq_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_alias_add(&format, "lnor"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return GPU_vertbuf_get_data(vbo); -} - -static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - if (mr->loop_normals) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]); - } - else { - if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); - } - else { - normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f)); - } - } - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index]; - if (mr->loop_normals) { - normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]); - } - else if (mp->flag & ME_SMOOTH) { - copy_v3_v3_short(&lnor_data->x, mr->mvert[ml->v].no); - } - else { - normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[mp_index]); - } - - /* Flag for paint mode overlay. - * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. - * In paint mode it will use the un-mapped data to draw the wire-frame. */ - if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && - (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { - lnor_data->w = -1; - } - else if (mp->flag & ME_FACE_SEL) { - lnor_data->w = 1; - } - else { - lnor_data->w = 0; - } - } -} - -static const MeshExtract extract_lnor_hq = { - .init = extract_lnor_hq_init, - .iter_poly_bm = extract_lnor_hq_iter_poly_bm, - .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; - -/** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract Loop Normal - * \{ */ - -static void *extract_lnor_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_alias_add(&format, "lnor"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return GPU_vertbuf_get_data(vbo); -} - -static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - if (mr->loop_normals) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); - } - else { - if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3( - bm_vert_no_get(mr, l_iter->v)); - } - else { - ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); - } - } - ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index]; - if (mr->loop_normals) { - *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]); - } - else if (mp->flag & ME_SMOOTH) { - *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[ml->v].no); - } - else { - *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[mp_index]); - } - - /* Flag for paint mode overlay. - * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. - * In paint mode it will use the un-mapped data to draw the wire-frame. */ - if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && - (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { - lnor_data->w = -1; - } - else if (mp->flag & ME_FACE_SEL) { - lnor_data->w = 1; - } - else { - lnor_data->w = 0; - } - } -} - -static const MeshExtract extract_lnor = { - .init = extract_lnor_init, - .iter_poly_bm = extract_lnor_iter_poly_bm, - .iter_poly_mesh = extract_lnor_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract UV layers - * \{ */ - -static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) -{ - GPUVertBuf *vbo = buf; - GPUVertFormat format = {0}; - GPU_vertformat_deinterleave(&format); - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - uint32_t uv_layers = cache->cd_used.uv; - /* HACK to fix T68857 */ - if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) { - int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV); - if (layer != -1) { - uv_layers |= (1 << layer); - } - } - - for (int i = 0; i < MAX_MTFACE; i++) { - if (uv_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); - - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - /* UV layer name. */ - BLI_snprintf(attr_name, sizeof(attr_name), "u%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - /* Auto layer name. */ - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); - /* Active render layer name. */ - if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "u"); - } - /* Active display layer name. */ - if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "au"); - /* Alias to `pos` for edit uvs. */ - GPU_vertformat_alias_add(&format, "pos"); - } - /* Stencil mask uv layer name. */ - if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "mu"); - } - } - } - - int v_len = mr->loop_len; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - /* VBO will not be used, only allocate minimum of memory. */ - v_len = 1; - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, v_len); - - float(*uv_data)[2] = (float(*)[2])GPU_vertbuf_get_data(vbo); - for (int i = 0; i < MAX_MTFACE; i++) { - if (uv_layers & (1 << i)) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); - memcpy(uv_data, luv->uv, sizeof(*uv_data)); - uv_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) { - memcpy(uv_data, layer_data->uv, sizeof(*uv_data)); - } - } - } - } - - return NULL; -} - -static const MeshExtract extract_uv = {.init = extract_uv_init, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Tangent layers - * \{ */ - -static void extract_tan_ex_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - GPUVertBuf *vbo, - const bool do_hq) -{ - GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; - GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; - - GPUVertFormat format = {0}; - GPU_vertformat_deinterleave(&format); - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - uint32_t tan_layers = cache->cd_used.tan; - float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO); - bool orco_allocated = false; - const bool use_orco_tan = cache->cd_used.tan_orco != 0; - - int tan_len = 0; - char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; - - for (int i = 0; i < MAX_MTFACE; i++) { - if (tan_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - /* Tangent layer name. */ - BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); - /* Active render layer name. */ - if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "t"); - } - /* Active display layer name. */ - if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "at"); - } - - BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME); - } - } - if (use_orco_tan && orco == NULL) { - /* If `orco` is not available compute it ourselves */ - orco_allocated = true; - orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMesh *bm = mr->bm; - for (int v = 0; v < mr->vert_len; v++) { - const BMVert *eve = BM_vert_at_index(bm, v); - /* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords. - * not the distorted ones. */ - copy_v3_v3(orco[v], eve->co); - } - } - else { - const MVert *mv = mr->mvert; - for (int v = 0; v < mr->vert_len; v++, mv++) { - copy_v3_v3(orco[v], mv->co); - } - } - BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0); - } - - /* Start Fresh */ - CustomData loop_data; - CustomData_reset(&loop_data); - if (tan_len != 0 || use_orco_tan) { - short tangent_mask = 0; - bool calc_active_tangent = false; - if (mr->extract_type == MR_EXTRACT_BMESH) { - BKE_editmesh_loop_tangent_calc(mr->edit_bmesh, - calc_active_tangent, - tangent_names, - tan_len, - mr->poly_normals, - mr->loop_normals, - orco, - &loop_data, - mr->loop_len, - &tangent_mask); - } - else { - BKE_mesh_calc_loop_tangent_ex(mr->mvert, - mr->mpoly, - mr->poly_len, - mr->mloop, - mr->mlooptri, - mr->tri_len, - cd_ldata, - calc_active_tangent, - tangent_names, - tan_len, - mr->poly_normals, - mr->loop_normals, - orco, - &loop_data, - mr->loop_len, - &tangent_mask); - } - } - - if (use_orco_tan) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); - GPU_vertformat_alias_add(&format, "t"); - GPU_vertformat_alias_add(&format, "at"); - } - - if (orco_allocated) { - MEM_SAFE_FREE(orco); - } - - int v_len = mr->loop_len; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - /* VBO will not be used, only allocate minimum of memory. */ - v_len = 1; - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, v_len); - - if (do_hq) { - short(*tan_data)[4] = (short(*)[4])GPU_vertbuf_get_data(vbo); - for (int i = 0; i < tan_len; i++) { - const char *name = tangent_names[i]; - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( - &loop_data, CD_TANGENT, name); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { - normal_float_to_short_v3(*tan_data, layer_data[ml_index]); - (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; - tan_data++; - } - } - if (use_orco_tan) { - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { - normal_float_to_short_v3(*tan_data, layer_data[ml_index]); - (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; - tan_data++; - } - } - } - else { - GPUPackedNormal *tan_data = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); - for (int i = 0; i < tan_len; i++) { - const char *name = tangent_names[i]; - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( - &loop_data, CD_TANGENT, name); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { - *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]); - tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2; - tan_data++; - } - } - if (use_orco_tan) { - float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { - *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]); - tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2; - tan_data++; - } - } - } - - CustomData_free(&loop_data, mr->loop_len); -} - -static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) -{ - extract_tan_ex_init(mr, cache, buf, false); - return NULL; -} - -static const MeshExtract extract_tan = {.init = extract_tan_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | - MR_DATA_LOOPTRI, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract HQ Tangent layers - * \{ */ - -static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) -{ - extract_tan_ex_init(mr, cache, buf, true); - return NULL; -} - -static const MeshExtract extract_tan_hq = { - .init = extract_tan_hq_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, - .use_threading = false, -}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Sculpt Data - * \{ */ - -static void *extract_sculpt_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - GPUVertFormat format = {0}; - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; - - float *cd_mask = CustomData_get_layer(cd_vdata, CD_PAINT_MASK); - int *cd_face_set = CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); - - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "fset", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - typedef struct gpuSculptData { - uint8_t face_set_color[4]; - float mask; - } gpuSculptData; - - gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo); - MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK); - int cd_face_set_ofs = CustomData_get_offset(cd_pdata, CD_SCULPT_FACE_SETS); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - float v_mask = 0.0f; - if (cd_mask) { - v_mask = BM_ELEM_CD_GET_FLOAT(l_iter->v, cd_mask_ofs); - } - vbo_data->mask = v_mask; - uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX}; - if (cd_face_set) { - const int face_set_id = BM_ELEM_CD_GET_INT(l_iter->f, cd_face_set_ofs); - if (face_set_id != mr->me->face_sets_color_default) { - BKE_paint_face_set_overlay_color_get( - face_set_id, mr->me->face_sets_color_seed, face_set_color); - } - } - copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color); - vbo_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - int mp_loop = 0; - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { - const MPoly *p = &mr->mpoly[mp_index]; - for (int l = 0; l < p->totloop; l++) { - float v_mask = 0.0f; - if (cd_mask) { - v_mask = cd_mask[loops[mp_loop].v]; - } - vbo_data->mask = v_mask; - - uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX}; - if (cd_face_set) { - const int face_set_id = cd_face_set[mp_index]; - /* Skip for the default color Face Set to render it white. */ - if (face_set_id != mr->me->face_sets_color_default) { - BKE_paint_face_set_overlay_color_get( - face_set_id, mr->me->face_sets_color_seed, face_set_color); - } - } - copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color); - mp_loop++; - vbo_data++; - } - } - } - - return NULL; -} - -static const MeshExtract extract_sculpt_data = { - .init = extract_sculpt_data_init, - .data_flag = 0, - /* TODO: enable threading. */ - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract VCol - * \{ */ - -static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) -{ - GPUVertBuf *vbo = buf; - GPUVertFormat format = {0}; - GPU_vertformat_deinterleave(&format); - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - uint32_t vcol_layers = cache->cd_used.vcol; - uint32_t svcol_layers = cache->cd_used.sculpt_vcol; - - for (int i = 0; i < MAX_MCOL; i++) { - if (vcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - - BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) { - GPU_vertformat_alias_add(&format, "ac"); - } - - /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 && - CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); - } - } - } - - /* Sculpt Vertex Colors */ - if (U.experimental.use_sculpt_vertex_colors) { - for (int i = 0; i < 8; i++) { - if (svcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - - BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "ac"); - } - /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs`. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); - } - } - } - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - typedef struct gpuMeshVcol { - ushort r, g, b, a; - } gpuMeshVcol; - - gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); - MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP); - - for (int i = 0; i < MAX_MCOL; i++) { - if (vcol_layers & (1 << i)) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); - vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); - vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); - vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); - vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); - vcol_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); - vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); - vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); - vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); - } - } - } - - if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs); - vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]); - vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]); - vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]); - vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]); - vcol_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]); - vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]); - vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]); - vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]); - } - } - } - } - return NULL; -} - -static const MeshExtract extract_vcol = { - .init = extract_vcol_init, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Orco - * \{ */ - -typedef struct MeshExtract_Orco_Data { - float (*vbo_data)[4]; - float (*orco)[3]; -} MeshExtract_Orco_Data; - -static void *extract_orco_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex - * attributes. This is a substantial waste of video-ram and should be done another way. - * Unfortunately, at the time of writing, I did not found any other "non disruptive" - * alternative. */ - GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - CustomData *cd_vdata = &mr->me->vdata; - - MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__); - data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo); - data->orco = CustomData_get_layer(cd_vdata, CD_ORCO); - /* Make sure `orco` layer was requested only if needed! */ - BLI_assert(data->orco); - return data; -} - -static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - float *loop_orco = orco_data->vbo_data[l_index]; - copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; - float *loop_orco = orco_data->vbo_data[ml_index]; - copy_v3_v3(loop_orco, orco_data->orco[ml->v]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } -} - -static void extract_orco_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_orco = { - .init = extract_orco_init, - .iter_poly_bm = extract_orco_iter_poly_bm, - .iter_poly_mesh = extract_orco_iter_poly_mesh, - .finish = extract_orco_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edge Factor - * Defines how much an edge is visible. - * \{ */ - -typedef struct MeshExtract_EdgeFac_Data { - uchar *vbo_data; - bool use_edge_render; - /* Number of loop per edge. */ - uchar edge_loop_count[0]; -} MeshExtract_EdgeFac_Data; - -static float loop_edge_factor_get(const float f_no[3], - const float v_co[3], - const float v_no[3], - const float v_next_co[3]) -{ - float enor[3], evec[3]; - sub_v3_v3v3(evec, v_next_co, v_co); - cross_v3_v3v3(enor, v_no, evec); - normalize_v3(enor); - float d = fabsf(dot_v3v3(enor, f_no)); - /* Re-scale to the slider range. */ - d *= (1.0f / 0.065f); - CLAMP(d, 0.0f, 1.0f); - return d; -} - -static void *extract_edge_fac_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - - MeshExtract_EdgeFac_Data *data; - - if (mr->extract_type == MR_EXTRACT_MESH) { - size_t edge_loop_count_size = sizeof(uint32_t) * mr->edge_len; - data = MEM_callocN(sizeof(*data) + edge_loop_count_size, __func__); - - /* HACK(fclem) Detecting the need for edge render. - * We could have a flag in the mesh instead or check the modifier stack. */ - const MEdge *med = mr->medge; - for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) { - if ((med->flag & ME_EDGERENDER) == 0) { - data->use_edge_render = true; - break; - } - } - } - else { - data = MEM_callocN(sizeof(*data), __func__); - /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */ - data->use_edge_render = true; - } - - data->vbo_data = GPU_vertbuf_get_data(vbo); - return data; -} - -static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_EdgeFac_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - if (BM_edge_is_manifold(l_iter->e)) { - float ratio = loop_edge_factor_get(bm_face_no_get(mr, f), - bm_vert_co_get(mr, l_iter->v), - bm_vert_no_get(mr, l_iter->v), - bm_vert_co_get(mr, l_iter->next->v)); - data->vbo_data[l_index] = ratio * 253 + 1; - } - else { - data->vbo_data[l_index] = 255; - } - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *_data) -{ - MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; - - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - if (data->use_edge_render) { - const MEdge *med = &mr->medge[ml->e]; - data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0; - } - else { - - /* Count loop per edge to detect non-manifold. */ - if (data->edge_loop_count[ml->e] < 3) { - data->edge_loop_count[ml->e]++; - } - if (data->edge_loop_count[ml->e] == 2) { - /* Manifold */ - const int ml_index_last = mp->totloop + mp->loopstart - 1; - const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); - const MLoop *ml_next = &mr->mloop[ml_index_other]; - const MVert *v1 = &mr->mvert[ml->v]; - const MVert *v2 = &mr->mvert[ml_next->v]; - float vnor_f[3]; - normal_short_to_float_v3(vnor_f, v1->no); - float ratio = loop_edge_factor_get(mr->poly_normals[mp_index], v1->co, vnor_f, v2->co); - data->vbo_data[ml_index] = ratio * 253 + 1; - } - else { - /* Non-manifold */ - data->vbo_data[ml_index] = 255; - } - } - } -} - -static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *UNUSED(eed), - const int ledge_index, - void *_data) -{ - MeshExtract_EdgeFac_Data *data = _data; - data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; - data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; -} - -static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *UNUSED(med), - const uint ledge_index, - void *_data) -{ - MeshExtract_EdgeFac_Data *data = _data; - - data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; - data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; -} - -static void extract_edge_fac_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *_data) -{ - GPUVertBuf *vbo = buf; - MeshExtract_EdgeFac_Data *data = _data; - - if (GPU_crappy_amd_driver()) { - /* Some AMD drivers strangely crash with VBO's with a one byte format. - * To workaround we reinitialize the VBO with another format and convert - * all bytes to floats. */ - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - } - /* We keep the data reference in data->vbo_data. */ - data->vbo_data = GPU_vertbuf_steal_data(vbo); - GPU_vertbuf_clear(vbo); - - int buf_len = mr->loop_len + mr->loop_loose_len; - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, buf_len); - - float *fdata = (float *)GPU_vertbuf_get_data(vbo); - for (int ml_index = 0; ml_index < buf_len; ml_index++, fdata++) { - *fdata = data->vbo_data[ml_index] / 255.0f; - } - /* Free old byte data. */ - MEM_freeN(data->vbo_data); - } - MEM_freeN(data); -} - -static const MeshExtract extract_edge_fac = { - .init = extract_edge_fac_init, - .iter_poly_bm = extract_edge_fac_iter_poly_bm, - .iter_poly_mesh = extract_edge_fac_iter_poly_mesh, - .iter_ledge_bm = extract_edge_fac_iter_ledge_bm, - .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh, - .finish = extract_edge_fac_finish, - .data_flag = MR_DATA_POLY_NOR, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; - -/** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract Vertex Weight - * \{ */ - -typedef struct MeshExtract_Weight_Data { - float *vbo_data; - const DRW_MeshWeightState *wstate; - const MDeformVert *dvert; /* For #Mesh. */ - int cd_ofs; /* For #BMesh. */ -} MeshExtract_Weight_Data; - -static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate) -{ - /* Error state. */ - if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) { - return -2.0f; - } - if (dvert == NULL) { - return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f; - } - - float input = 0.0f; - if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) { - /* Multi-Paint feature */ - bool is_normalized = (wstate->flags & (DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE | - DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE)); - input = BKE_defvert_multipaint_collective_weight(dvert, - wstate->defgroup_len, - wstate->defgroup_sel, - wstate->defgroup_sel_count, - is_normalized); - /* make it black if the selected groups have no weight on a vertex */ - if (input == 0.0f) { - return -1.0f; - } - } - else { - /* default, non tricky behavior */ - input = BKE_defvert_find_weight(dvert, wstate->defgroup_active); - - if (input == 0.0f) { - switch (wstate->alert_mode) { - case OB_DRAW_GROUPUSER_ACTIVE: - return -1.0f; - break; - case OB_DRAW_GROUPUSER_ALL: - if (BKE_defvert_is_weight_zero(dvert, wstate->defgroup_len)) { - return -1.0f; - } - break; - } - } - } - - /* Lock-Relative: display the fraction of current weight vs total unlocked weight. */ - if (wstate->flags & DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE) { - input = BKE_defvert_lock_relative_weight( - input, dvert, wstate->defgroup_len, wstate->defgroup_locked, wstate->defgroup_unlocked); - } - - CLAMP(input, 0.0f, 1.0f); - return input; -} - -static void *extract_weights_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - - MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (float *)GPU_vertbuf_get_data(vbo); - data->wstate = &cache->weight_state; - - if (data->wstate->defgroup_active == -1) { - /* Nothing to show. */ - data->dvert = NULL; - data->cd_ofs = -1; - } - else if (mr->extract_type == MR_EXTRACT_BMESH) { - data->dvert = NULL; - data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT); - } - else { - data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT); - data->cd_ofs = -1; - } - return data; -} - -static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_Weight_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - if (data->cd_ofs != -1) { - const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs); - data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate); - } - else { - data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate); - } - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_Weight_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - if (data->dvert != NULL) { - const MDeformVert *dvert = &data->dvert[ml->v]; - data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); - } - else { - const MDeformVert *dvert = NULL; - data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); - } - } -} - -static void extract_weights_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_weights = { - .init = extract_weights_init, - .iter_poly_bm = extract_weights_iter_poly_bm, - .iter_poly_mesh = extract_weights_iter_poly_mesh, - .finish = extract_weights_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit Mode Data / Flags - * \{ */ - -typedef struct EditLoopData { - uchar v_flag; - uchar e_flag; - uchar crease; - uchar bweight; -} EditLoopData; - -static void mesh_render_data_face_flag(const MeshRenderData *mr, - BMFace *efa, - const int cd_ofs, - EditLoopData *eattr) -{ - if (efa == mr->efa_act) { - eattr->v_flag |= VFLAG_FACE_ACTIVE; - } - if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - eattr->v_flag |= VFLAG_FACE_SELECTED; - } - - if (efa == mr->efa_act_uv) { - eattr->v_flag |= VFLAG_FACE_UV_ACTIVE; - } - if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) { - eattr->v_flag |= VFLAG_FACE_UV_SELECT; - } - -#ifdef WITH_FREESTYLE - if (mr->freestyle_face_ofs != -1) { - const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs); - if (ffa->flag & FREESTYLE_FACE_MARK) { - eattr->v_flag |= VFLAG_FACE_FREESTYLE; - } - } -#endif -} - -static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, EditLoopData *eattr) -{ - const ToolSettings *ts = mr->toolsettings; - const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0; - const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE); - - if (eed == mr->eed_act) { - eattr->e_flag |= VFLAG_EDGE_ACTIVE; - } - if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - eattr->e_flag |= VFLAG_EDGE_SELECTED; - } - if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) && - BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) { - eattr->e_flag |= VFLAG_EDGE_SELECTED; - eattr->e_flag |= VFLAG_VERT_SELECTED; - } - if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) { - eattr->e_flag |= VFLAG_EDGE_SEAM; - } - if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) { - eattr->e_flag |= VFLAG_EDGE_SHARP; - } - - /* Use active edge color for active face edges because - * specular highlights make it hard to see T55456#510873. - * - * This isn't ideal since it can't be used when mixing edge/face modes - * but it's still better than not being able to see the active face. */ - if (is_face_only_select_mode) { - if (mr->efa_act != NULL) { - if (BM_edge_in_face(eed, mr->efa_act)) { - eattr->e_flag |= VFLAG_EDGE_ACTIVE; - } - } - } - - /* Use a byte for value range */ - if (mr->crease_ofs != -1) { - float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs); - if (crease > 0) { - eattr->crease = (uchar)(crease * 255.0f); - } - } - /* Use a byte for value range */ - if (mr->bweight_ofs != -1) { - float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs); - if (bweight > 0) { - eattr->bweight = (uchar)(bweight * 255.0f); - } - } -#ifdef WITH_FREESTYLE - if (mr->freestyle_edge_ofs != -1) { - const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs); - if (fed->flag & FREESTYLE_EDGE_MARK) { - eattr->e_flag |= VFLAG_EDGE_FREESTYLE; - } - } -#endif -} - -static void mesh_render_data_loop_flag(const MeshRenderData *mr, - BMLoop *l, - const int cd_ofs, - EditLoopData *eattr) -{ - if (cd_ofs == -1) { - return; - } - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_ofs); - if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) { - eattr->v_flag |= VFLAG_VERT_UV_PINNED; - } - if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) { - eattr->v_flag |= VFLAG_VERT_UV_SELECT; - } -} - -static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr, - BMLoop *l, - const int cd_ofs, - EditLoopData *eattr) -{ - if (cd_ofs == -1) { - return; - } - if (uvedit_edge_select_test_ex(mr->toolsettings, l, cd_ofs)) { - eattr->v_flag |= VFLAG_EDGE_UV_SELECT; - eattr->v_flag |= VFLAG_VERT_UV_SELECT; - } -} - -static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, EditLoopData *eattr) -{ - if (eve == mr->eve_act) { - eattr->e_flag |= VFLAG_VERT_ACTIVE; - } - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - eattr->e_flag |= VFLAG_VERT_SELECTED; - } -} - -static void *extract_edit_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* WARNING: Adjust #EditLoopData struct accordingly. */ - GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); - GPU_vertformat_alias_add(&format, "flag"); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - return GPU_vertbuf_get_data(vbo); -} - -static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - EditLoopData *data = (EditLoopData *)_data + l_index; - memset(data, 0x0, sizeof(*data)); - mesh_render_data_face_flag(mr, f, -1, data); - mesh_render_data_edge_flag(mr, l_iter->e, data); - mesh_render_data_vert_flag(mr, l_iter->v, data); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *_data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - EditLoopData *data = (EditLoopData *)_data + ml_index; - memset(data, 0x0, sizeof(*data)); - BMFace *efa = bm_original_face_get(mr, mp_index); - BMEdge *eed = bm_original_edge_get(mr, ml->e); - BMVert *eve = bm_original_vert_get(mr, ml->v); - if (efa) { - mesh_render_data_face_flag(mr, efa, -1, data); - } - if (eed) { - mesh_render_data_edge_flag(mr, eed, data); - } - if (eve) { - mesh_render_data_vert_flag(mr, eve, data); - } - } -} - -static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *_data) -{ - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); - memset(data, 0x0, sizeof(*data) * 2); - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - mesh_render_data_vert_flag(mr, eed->v1, &data[0]); - mesh_render_data_vert_flag(mr, eed->v2, &data[1]); -} - -static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *_data) -{ - EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; - memset(data, 0x0, sizeof(*data) * 2); - const int e_index = mr->ledges[ledge_index]; - BMEdge *eed = bm_original_edge_get(mr, e_index); - BMVert *eve1 = bm_original_vert_get(mr, med->v1); - BMVert *eve2 = bm_original_vert_get(mr, med->v2); - if (eed) { - mesh_render_data_edge_flag(mr, eed, &data[0]); - data[1] = data[0]; - } - if (eve1) { - mesh_render_data_vert_flag(mr, eve1, &data[0]); - } - if (eve2) { - mesh_render_data_vert_flag(mr, eve2, &data[1]); - } -} - -static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *_data) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - mesh_render_data_vert_flag(mr, eve, data); -} - -static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *UNUSED(mv), - const int lvert_index, - void *_data) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; - memset(data, 0x0, sizeof(*data)); - const int v_index = mr->lverts[lvert_index]; - BMVert *eve = bm_original_vert_get(mr, v_index); - if (eve) { - mesh_render_data_vert_flag(mr, eve, data); - } -} - -static const MeshExtract extract_edit_data = { - .init = extract_edit_data_init, - .iter_poly_bm = extract_edit_data_iter_poly_bm, - .iter_poly_mesh = extract_edit_data_iter_poly_mesh, - .iter_ledge_bm = extract_edit_data_iter_ledge_bm, - .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh, - .iter_lvert_bm = extract_edit_data_iter_lvert_bm, - .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Data / Flags - * \{ */ - -typedef struct MeshExtract_EditUVData_Data { - EditLoopData *vbo_data; - int cd_ofs; -} MeshExtract_EditUVData_Data; - -static void *extract_edituv_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* WARNING: Adjust #EditLoopData struct accordingly. */ - GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); - GPU_vertformat_alias_add(&format, "flag"); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - - MeshExtract_EditUVData_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); - data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); - return data; -} - -static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - MeshExtract_EditUVData_Data *data = _data; - EditLoopData *eldata = &data->vbo_data[l_index]; - memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata); - mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); - mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *_data) -{ - MeshExtract_EditUVData_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - - EditLoopData *eldata = &data->vbo_data[ml_index]; - memset(eldata, 0x0, sizeof(*eldata)); - BMFace *efa = bm_original_face_get(mr, mp_index); - if (efa) { - BMEdge *eed = bm_original_edge_get(mr, ml->e); - BMVert *eve = bm_original_vert_get(mr, ml->v); - if (eed && eve) { - /* Loop on an edge endpoint. */ - BMLoop *l = BM_face_edge_share_loop(efa, eed); - mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata); - mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); - } - else { - if (eed == NULL) { - /* Find if the loop's vert is not part of an edit edge. - * For this, we check if the previous loop was on an edge. */ - const int ml_index_last = mp->loopstart + mp->totloop - 1; - const int l_prev = (ml_index == mp->loopstart) ? ml_index_last : (ml_index - 1); - const MLoop *ml_prev = &mr->mloop[l_prev]; - eed = bm_original_edge_get(mr, ml_prev->e); - } - if (eed) { - /* Mapped points on an edge between two edit verts. */ - BMLoop *l = BM_face_edge_share_loop(efa, eed); - mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); - } - } - } - } -} - -static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_edituv_data = { - .init = extract_edituv_data_init, - .iter_poly_bm = extract_edituv_data_iter_poly_bm, - .iter_poly_mesh = extract_edituv_data_iter_poly_mesh, - .finish = extract_edituv_data_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV area stretch - * \{ */ - -static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return NULL; -} - -BLI_INLINE float area_ratio_get(float area, float uvarea) -{ - if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) { - /* Tag inversion by using the sign. */ - return (area > uvarea) ? (uvarea / area) : -(area / uvarea); - } - return 0.0f; -} - -BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio) -{ - ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio; - return (ratio > 1.0f) ? (1.0f / ratio) : ratio; -} - -static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - void *buf, - void *UNUSED(data)) -{ - GPUVertBuf *vbo = buf; - float tot_area = 0.0f, tot_uv_area = 0.0f; - float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - CustomData *cd_ldata = &mr->bm->ldata; - int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); - - BMFace *efa; - BMIter f_iter; - int f; - BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { - float area = BM_face_calc_area(efa); - float uvarea = BM_face_calc_area_uv(efa, uv_ofs); - tot_area += area; - tot_uv_area += uvarea; - area_ratio[f] = area_ratio_get(area, uvarea); - } - } - else { - BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); - const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); - const MPoly *mp = mr->mpoly; - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - float area = BKE_mesh_calc_poly_area(mp, &mr->mloop[mp->loopstart], mr->mvert); - float uvarea = BKE_mesh_calc_poly_uv_area(mp, uv_data); - tot_area += area; - tot_uv_area += uvarea; - area_ratio[mp_index] = area_ratio_get(area, uvarea); - } - } - - cache->tot_area = tot_area; - cache->tot_uv_area = tot_uv_area; - - /* Convert in place to avoid an extra allocation */ - uint16_t *poly_stretch = (uint16_t *)area_ratio; - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { - poly_stretch[mp_index] = area_ratio[mp_index] * SHRT_MAX; - } - - /* Copy face data for each loop. */ - uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMFace *efa; - BMIter f_iter; - int f, l_index = 0; - BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { - for (int i = 0; i < efa->len; i++, l_index++) { - loop_stretch[l_index] = poly_stretch[f]; - } - } - } - else { - BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); - const MPoly *mp = mr->mpoly; - for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - for (int i = 0; i < mp->totloop; i++, l_index++) { - loop_stretch[l_index] = poly_stretch[mp_index]; - } - } - } - - MEM_freeN(area_ratio); -} - -static const MeshExtract extract_edituv_stretch_area = { - .init = extract_edituv_stretch_area_init, - .finish = extract_edituv_stretch_area_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV angle stretch - * \{ */ - -typedef struct UVStretchAngle { - int16_t angle; - int16_t uv_angles[2]; -} UVStretchAngle; - -typedef struct MeshExtract_StretchAngle_Data { - UVStretchAngle *vbo_data; - MLoopUV *luv; - float auv[2][2], last_auv[2]; - float av[2][3], last_av[3]; - int cd_ofs; -} MeshExtract_StretchAngle_Data; - -static void compute_normalize_edge_vectors(float auv[2][2], - float av[2][3], - const float uv[2], - const float uv_prev[2], - const float co[3], - const float co_prev[3]) -{ - /* Move previous edge. */ - copy_v2_v2(auv[0], auv[1]); - copy_v3_v3(av[0], av[1]); - /* 2d edge */ - sub_v2_v2v2(auv[1], uv_prev, uv); - normalize_v2(auv[1]); - /* 3d edge */ - sub_v3_v3v3(av[1], co_prev, co); - normalize_v3(av[1]); -} - -static short v2_to_short_angle(const float v[2]) -{ - return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX; -} - -static void edituv_get_edituv_stretch_angle(float auv[2][2], - const float av[2][3], - UVStretchAngle *r_stretch) -{ - /* Send UV's to the shader and let it compute the aspect corrected angle. */ - r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]); - r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]); - /* Compute 3D angle here. */ - r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX; - -#if 0 /* here for reference, this is done in shader now. */ - float uvang = angle_normalized_v2v2(auv0, auv1); - float ang = angle_normalized_v3v3(av0, av1); - float stretch = fabsf(uvang - ang) / (float)M_PI; - return 1.0f - pow2f(1.0f - stretch); -#endif -} - -static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* Waning: adjust #UVStretchAngle struct accordingly. */ - GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); - GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo); - - /* Special iterator needed to save about half of the computing cost. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); - } - else { - BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); - data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); - } - return data; -} - -static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_StretchAngle_Data *data = _data; - float(*auv)[2] = data->auv, *last_auv = data->last_auv; - float(*av)[3] = data->av, *last_av = data->last_av; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - - const MLoopUV *luv, *luv_next; - BMLoop *l_next = l_iter->next; - if (l_iter == BM_FACE_FIRST_LOOP(f)) { - /* First loop in face. */ - BMLoop *l_tmp = l_iter->prev; - BMLoop *l_next_tmp = l_iter; - luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs); - luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs); - compute_normalize_edge_vectors(auv, - av, - luv->uv, - luv_next->uv, - bm_vert_co_get(mr, l_tmp->v), - bm_vert_co_get(mr, l_next_tmp->v)); - /* Save last edge. */ - copy_v2_v2(last_auv, auv[1]); - copy_v3_v3(last_av, av[1]); - } - if (l_next == BM_FACE_FIRST_LOOP(f)) { - /* Move previous edge. */ - copy_v2_v2(auv[0], auv[1]); - copy_v3_v3(av[0], av[1]); - /* Copy already calculated last edge. */ - copy_v2_v2(auv[1], last_auv); - copy_v3_v3(av[1], last_av); - } - else { - luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); - luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs); - compute_normalize_edge_vectors(auv, - av, - luv->uv, - luv_next->uv, - bm_vert_co_get(mr, l_iter->v), - bm_vert_co_get(mr, l_next->v)); - } - edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *_data) -{ - MeshExtract_StretchAngle_Data *data = _data; - - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - float(*auv)[2] = data->auv, *last_auv = data->last_auv; - float(*av)[3] = data->av, *last_av = data->last_av; - int l_next = ml_index + 1; - const MVert *v, *v_next; - if (ml_index == mp->loopstart) { - /* First loop in face. */ - const int ml_index_last = ml_index_end - 1; - const int l_next_tmp = mp->loopstart; - v = &mr->mvert[mr->mloop[ml_index_last].v]; - v_next = &mr->mvert[mr->mloop[l_next_tmp].v]; - compute_normalize_edge_vectors( - auv, av, data->luv[ml_index_last].uv, data->luv[l_next_tmp].uv, v->co, v_next->co); - /* Save last edge. */ - copy_v2_v2(last_auv, auv[1]); - copy_v3_v3(last_av, av[1]); - } - if (l_next == ml_index_end) { - l_next = mp->loopstart; - /* Move previous edge. */ - copy_v2_v2(auv[0], auv[1]); - copy_v3_v3(av[0], av[1]); - /* Copy already calculated last edge. */ - copy_v2_v2(auv[1], last_auv); - copy_v3_v3(av[1], last_av); - } - else { - v = &mr->mvert[mr->mloop[ml_index].v]; - v_next = &mr->mvert[mr->mloop[l_next].v]; - compute_normalize_edge_vectors( - auv, av, data->luv[ml_index].uv, data->luv[l_next].uv, v->co, v_next->co); - } - edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]); - } -} - -static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_edituv_stretch_angle = { - .init = extract_edituv_stretch_angle_init, - .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm, - .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh, - .finish = extract_edituv_stretch_angle_finish, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Edit Mesh Analysis Colors - * \{ */ - -static void *extract_mesh_analysis_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len); - - return NULL; -} - -static void axis_from_enum_v3(float v[3], const char axis) -{ - zero_v3(v); - if (axis < 3) { - v[axis] = 1.0f; - } - else { - v[axis - 3] = -1.0f; - } -} - -BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange) -{ - if (fac < min) { - fac = 1.0f; - } - else if (fac > max) { - fac = -1.0f; - } - else { - fac = (fac - min) * minmax_irange; - fac = 1.0f - fac; - CLAMP(fac, 0.0f, 1.0f); - } - return fac; -} - -static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang) -{ - const MeshStatVis *statvis = &mr->toolsettings->statvis; - const float min = statvis->overhang_min / (float)M_PI; - const float max = statvis->overhang_max / (float)M_PI; - const char axis = statvis->overhang_axis; - BMEditMesh *em = mr->edit_bmesh; - BMIter iter; - BMesh *bm = em->bm; - BMFace *f; - float dir[3]; - const float minmax_irange = 1.0f / (max - min); - - BLI_assert(min <= max); - - axis_from_enum_v3(dir, axis); - - /* now convert into global space */ - mul_transposed_mat3_m4_v3(mr->obmat, dir); - normalize_v3(dir); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - int l_index = 0; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / (float)M_PI; - fac = overhang_remap(fac, min, max, minmax_irange); - for (int i = 0; i < f->len; i++, l_index++) { - r_overhang[l_index] = fac; - } - } - } - else { - const MPoly *mp = mr->mpoly; - for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - float fac = angle_normalized_v3v3(mr->poly_normals[mp_index], dir) / (float)M_PI; - fac = overhang_remap(fac, min, max, minmax_irange); - for (int i = 0; i < mp->totloop; i++, l_index++) { - r_overhang[l_index] = fac; - } - } - } -} - -/** - * Needed so we can use jitter values for face interpolation. - */ -static void uv_from_jitter_v2(float uv[2]) -{ - uv[0] += 0.5f; - uv[1] += 0.5f; - if (uv[0] + uv[1] > 1.0f) { - uv[0] = 1.0f - uv[0]; - uv[1] = 1.0f - uv[1]; - } - - clamp_v2(uv, 0.0f, 1.0f); -} - -BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange) -{ - /* important not '<=' */ - if (fac < max) { - fac = (fac - min) * minmax_irange; - fac = 1.0f - fac; - CLAMP(fac, 0.0f, 1.0f); - } - else { - fac = -1.0f; - } - return fac; -} - -static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness) -{ - const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */ - /* cheating to avoid another allocation */ - float *face_dists = r_thickness + (mr->loop_len - mr->poly_len); - BMEditMesh *em = mr->edit_bmesh; - const float scale = 1.0f / mat4_to_scale(mr->obmat); - const MeshStatVis *statvis = &mr->toolsettings->statvis; - const float min = statvis->thickness_min * scale; - const float max = statvis->thickness_max * scale; - const float minmax_irange = 1.0f / (max - min); - const int samples = statvis->thickness_samples; - float jit_ofs[32][2]; - BLI_assert(samples <= 32); - BLI_assert(min <= max); - - copy_vn_fl(face_dists, mr->poly_len, max); - - BLI_jitter_init(jit_ofs, samples); - for (int j = 0; j < samples; j++) { - uv_from_jitter_v2(jit_ofs[j]); - } - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMesh *bm = em->bm; - BM_mesh_elem_index_ensure(bm, BM_FACE); - - struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); - struct BMLoop *(*looptris)[3] = em->looptris; - for (int i = 0; i < mr->tri_len; i++) { - BMLoop **ltri = looptris[i]; - const int index = BM_elem_index_get(ltri[0]->f); - const float *cos[3] = { - bm_vert_co_get(mr, ltri[0]->v), - bm_vert_co_get(mr, ltri[1]->v), - bm_vert_co_get(mr, ltri[2]->v), - }; - float ray_co[3]; - float ray_no[3]; - - normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); - - for (int j = 0; j < samples; j++) { - float dist = face_dists[index]; - interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); - madd_v3_v3fl(ray_co, ray_no, eps_offset); - - BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL); - if (f_hit && dist < face_dists[index]) { - float angle_fac = fabsf( - dot_v3v3(bm_face_no_get(mr, ltri[0]->f), bm_face_no_get(mr, f_hit))); - angle_fac = 1.0f - angle_fac; - angle_fac = angle_fac * angle_fac * angle_fac; - angle_fac = 1.0f - angle_fac; - dist /= angle_fac; - if (dist < face_dists[index]) { - face_dists[index] = dist; - } - } - } - } - BKE_bmbvh_free(bmtree); - - BMIter iter; - BMFace *f; - int l_index = 0; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - float fac = face_dists[BM_elem_index_get(f)]; - fac = thickness_remap(fac, min, max, minmax_irange); - for (int i = 0; i < f->len; i++, l_index++) { - r_thickness[l_index] = fac; - } - } - } - else { - BVHTreeFromMesh treeData = {NULL}; - - BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); - const MLoopTri *mlooptri = mr->mlooptri; - for (int i = 0; i < mr->tri_len; i++, mlooptri++) { - const int index = mlooptri->poly; - const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co, - mr->mvert[mr->mloop[mlooptri->tri[1]].v].co, - mr->mvert[mr->mloop[mlooptri->tri[2]].v].co}; - float ray_co[3]; - float ray_no[3]; - - normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); - - for (int j = 0; j < samples; j++) { - interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); - madd_v3_v3fl(ray_co, ray_no, eps_offset); - - BVHTreeRayHit hit; - hit.index = -1; - hit.dist = face_dists[index]; - if ((BLI_bvhtree_ray_cast( - tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) && - hit.dist < face_dists[index]) { - float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no)); - angle_fac = 1.0f - angle_fac; - angle_fac = angle_fac * angle_fac * angle_fac; - angle_fac = 1.0f - angle_fac; - hit.dist /= angle_fac; - if (hit.dist < face_dists[index]) { - face_dists[index] = hit.dist; - } - } - } - } - - const MPoly *mp = mr->mpoly; - for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - float fac = face_dists[mp_index]; - fac = thickness_remap(fac, min, max, minmax_irange); - for (int i = 0; i < mp->totloop; i++, l_index++) { - r_thickness[l_index] = fac; - } - } - } -} - -struct BVHTree_OverlapData { - const Mesh *me; - const MLoopTri *mlooptri; - float epsilon; -}; - -static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) -{ - struct BVHTree_OverlapData *data = userdata; - const Mesh *me = data->me; - - const MLoopTri *tri_a = &data->mlooptri[index_a]; - const MLoopTri *tri_b = &data->mlooptri[index_b]; - - if (UNLIKELY(tri_a->poly == tri_b->poly)) { - return false; - } - - const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co, - me->mvert[me->mloop[tri_a->tri[1]].v].co, - me->mvert[me->mloop[tri_a->tri[2]].v].co}; - const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co, - me->mvert[me->mloop[tri_b->tri[1]].v].co, - me->mvert[me->mloop[tri_b->tri[2]].v].co}; - float ix_pair[2][3]; - int verts_shared = 0; - - verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) + - ELEM(tri_a_co[2], UNPACK3(tri_b_co))); - - /* if 2 points are shared, bail out */ - if (verts_shared >= 2) { - return false; - } - - return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) && - /* if we share a vertex, check the intersection isn't a 'point' */ - ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon))); -} - -static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect) -{ - BMEditMesh *em = mr->edit_bmesh; - - for (int l_index = 0; l_index < mr->loop_len; l_index++) { - r_intersect[l_index] = -1.0f; - } - - if (mr->extract_type == MR_EXTRACT_BMESH) { - uint overlap_len; - BMesh *bm = em->bm; - - BM_mesh_elem_index_ensure(bm, BM_FACE); - - struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); - BVHTreeOverlap *overlap = BKE_bmbvh_overlap_self(bmtree, &overlap_len); - - if (overlap) { - for (int i = 0; i < overlap_len; i++) { - BMFace *f_hit_pair[2] = { - em->looptris[overlap[i].indexA][0]->f, - em->looptris[overlap[i].indexB][0]->f, - }; - for (int j = 0; j < 2; j++) { - BMFace *f_hit = f_hit_pair[j]; - BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit); - int l_index = BM_elem_index_get(l_first); - for (int k = 0; k < f_hit->len; k++, l_index++) { - r_intersect[l_index] = 1.0f; - } - } - } - MEM_freeN(overlap); - } - - BKE_bmbvh_free(bmtree); - } - else { - uint overlap_len; - BVHTreeFromMesh treeData = {NULL}; - - BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); - - struct BVHTree_OverlapData data = { - .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)}; - - BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data); - if (overlap) { - for (int i = 0; i < overlap_len; i++) { - const MPoly *f_hit_pair[2] = { - &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly], - &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly], - }; - for (int j = 0; j < 2; j++) { - const MPoly *f_hit = f_hit_pair[j]; - int l_index = f_hit->loopstart; - for (int k = 0; k < f_hit->totloop; k++, l_index++) { - r_intersect[l_index] = 1.0f; - } - } - } - MEM_freeN(overlap); - } - } -} - -BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange) -{ - if (fac >= min) { - fac = (fac - min) * minmax_irange; - CLAMP(fac, 0.0f, 1.0f); - } - else { - /* fallback */ - fac = -1.0f; - } - return fac; -} - -static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort) -{ - BMEditMesh *em = mr->edit_bmesh; - const MeshStatVis *statvis = &mr->toolsettings->statvis; - const float min = statvis->distort_min; - const float max = statvis->distort_max; - const float minmax_irange = 1.0f / (max - min); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMesh *bm = em->bm; - BMFace *f; - - if (mr->bm_vert_coords != NULL) { - BKE_editmesh_cache_ensure_poly_normals(em, mr->edit_data); - - /* Most likely this is already valid, ensure just in case. - * Needed for #BM_loop_calc_face_normal_safe_vcos. */ - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - } - - int l_index = 0; - int f_index = 0; - BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) { - float fac = -1.0f; - - if (f->len > 3) { - BMLoop *l_iter, *l_first; - - fac = 0.0f; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const float *no_face; - float no_corner[3]; - if (mr->bm_vert_coords != NULL) { - no_face = mr->bm_poly_normals[f_index]; - BM_loop_calc_face_normal_safe_vcos(l_iter, no_face, mr->bm_vert_coords, no_corner); - } - else { - no_face = f->no; - BM_loop_calc_face_normal_safe(l_iter, no_corner); - } - - /* simple way to detect (what is most likely) concave */ - if (dot_v3v3(no_face, no_corner) < 0.0f) { - negate_v3(no_corner); - } - fac = max_ff(fac, angle_normalized_v3v3(no_face, no_corner)); - - } while ((l_iter = l_iter->next) != l_first); - fac *= 2.0f; - } - - fac = distort_remap(fac, min, max, minmax_irange); - for (int i = 0; i < f->len; i++, l_index++) { - r_distort[l_index] = fac; - } - } - } - else { - const MPoly *mp = mr->mpoly; - for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - float fac = -1.0f; - - if (mp->totloop > 3) { - float *f_no = mr->poly_normals[mp_index]; - fac = 0.0f; - - for (int i = 1; i <= mp->totloop; i++) { - const MLoop *l_prev = &mr->mloop[mp->loopstart + (i - 1) % mp->totloop]; - const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop]; - const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop]; - float no_corner[3]; - normal_tri_v3(no_corner, - mr->mvert[l_prev->v].co, - mr->mvert[l_curr->v].co, - mr->mvert[l_next->v].co); - /* simple way to detect (what is most likely) concave */ - if (dot_v3v3(f_no, no_corner) < 0.0f) { - negate_v3(no_corner); - } - fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner)); - } - fac *= 2.0f; - } - - fac = distort_remap(fac, min, max, minmax_irange); - for (int i = 0; i < mp->totloop; i++, l_index++) { - r_distort[l_index] = fac; - } - } - } -} - -BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange) -{ - /* important not '>=' */ - if (fac > min) { - fac = (fac - min) * minmax_irange; - CLAMP(fac, 0.0f, 1.0f); - } - else { - /* fallback */ - fac = -1.0f; - } - return fac; -} - -static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) -{ - BMEditMesh *em = mr->edit_bmesh; - const MeshStatVis *statvis = &mr->toolsettings->statvis; - const float min = statvis->sharp_min; - const float max = statvis->sharp_max; - const float minmax_irange = 1.0f / (max - min); - - /* Can we avoid this extra allocation? */ - float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__); - copy_vn_fl(vert_angles, mr->vert_len, -M_PI); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - BMIter iter; - BMesh *bm = em->bm; - BMFace *efa; - BMEdge *e; - /* first assign float values to verts */ - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - float angle = BM_edge_calc_face_angle_signed(e); - float *col1 = &vert_angles[BM_elem_index_get(e->v1)]; - float *col2 = &vert_angles[BM_elem_index_get(e->v2)]; - *col1 = max_ff(*col1, angle); - *col2 = max_ff(*col2, angle); - } - /* Copy vert value to loops. */ - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - int l_index = BM_elem_index_get(l_iter); - int v_index = BM_elem_index_get(l_iter->v); - r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange); - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - /* first assign float values to verts */ - const MPoly *mp = mr->mpoly; - - EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len); - - for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { - for (int i = 0; i < mp->totloop; i++) { - const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop]; - const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop]; - const MVert *v_curr = &mr->mvert[l_curr->v]; - const MVert *v_next = &mr->mvert[l_next->v]; - float angle; - void **pval; - bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval); - if (!value_is_init) { - *pval = mr->poly_normals[mp_index]; - /* non-manifold edge, yet... */ - continue; - } - if (*pval != NULL) { - const float *f1_no = mr->poly_normals[mp_index]; - const float *f2_no = *pval; - angle = angle_normalized_v3v3(f1_no, f2_no); - angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle; - /* Tag as manifold. */ - *pval = NULL; - } - else { - /* non-manifold edge */ - angle = DEG2RADF(90.0f); - } - float *col1 = &vert_angles[l_curr->v]; - float *col2 = &vert_angles[l_next->v]; - *col1 = max_ff(*col1, angle); - *col2 = max_ff(*col2, angle); - } - } - /* Remaining non manifold edges. */ - EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh); - for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { - if (BLI_edgehashIterator_getValue(ehi) != NULL) { - uint v1, v2; - const float angle = DEG2RADF(90.0f); - BLI_edgehashIterator_getKey(ehi, &v1, &v2); - float *col1 = &vert_angles[v1]; - float *col2 = &vert_angles[v2]; - *col1 = max_ff(*col1, angle); - *col2 = max_ff(*col2, angle); - } - } - BLI_edgehashIterator_free(ehi); - BLI_edgehash_free(eh, NULL); - - const MLoop *ml = mr->mloop; - for (int l_index = 0; l_index < mr->loop_len; l_index++, ml++) { - r_sharp[l_index] = sharp_remap(vert_angles[ml->v], min, max, minmax_irange); - } - } - - MEM_freeN(vert_angles); -} - -static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) -{ - GPUVertBuf *vbo = buf; - BLI_assert(mr->edit_bmesh); - - float *l_weight = (float *)GPU_vertbuf_get_data(vbo); - - switch (mr->toolsettings->statvis.type) { - case SCE_STATVIS_OVERHANG: - statvis_calc_overhang(mr, l_weight); - break; - case SCE_STATVIS_THICKNESS: - statvis_calc_thickness(mr, l_weight); - break; - case SCE_STATVIS_INTERSECT: - statvis_calc_intersect(mr, l_weight); - break; - case SCE_STATVIS_DISTORT: - statvis_calc_distort(mr, l_weight); - break; - case SCE_STATVIS_SHARP: - statvis_calc_sharp(mr, l_weight); - break; - } -} - -static const MeshExtract extract_mesh_analysis = { - .init = extract_mesh_analysis_init, - .finish = extract_analysis_iter_finish_mesh, - /* This is not needed for all visualization types. - * * Maybe split into different extract. */ - .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots positions - * \{ */ - -static void *extract_fdots_pos_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); - return GPU_vertbuf_get_data(vbo); -} - -static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int f_index, - void *data) -{ - float(*center)[3] = data; - - float *co = center[f_index]; - zero_v3(co); - - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); - } while ((l_iter = l_iter->next) != l_first); - mul_v3_fl(co, 1.0f / (float)f->len); -} - -static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - float(*center)[3] = (float(*)[3])data; - float *co = center[mp_index]; - zero_v3(co); - - const MVert *mvert = mr->mvert; - const MLoop *mloop = mr->mloop; - - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - if (mr->use_subsurf_fdots) { - const MVert *mv = &mr->mvert[ml->v]; - if (mv->flag & ME_VERT_FACEDOT) { - copy_v3_v3(center[mp_index], mv->co); - break; - } - } - else { - const MVert *mv = &mvert[ml->v]; - add_v3_v3(center[mp_index], mv->co); - } - } - - if (!mr->use_subsurf_fdots) { - mul_v3_fl(co, 1.0f / (float)mp->totloop); - } -} - -static const MeshExtract extract_fdots_pos = { - .init = extract_fdots_pos_init, - .iter_poly_bm = extract_fdots_pos_iter_poly_bm, - .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; - -/** \} */ +#include "atomic_ops.h" -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Normal and edit flag - * \{ */ -#define NOR_AND_FLAG_DEFAULT 0 -#define NOR_AND_FLAG_SELECT 1 -#define NOR_AND_FLAG_ACTIVE -1 -#define NOR_AND_FLAG_HIDDEN -2 +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" -static void *extract_fdots_nor_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - } +#include "BLI_task.h" - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); +#include "BKE_editmesh.h" - return NULL; -} +#include "GPU_capabilities.h" -static void extract_fdots_nor_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) -{ - GPUVertBuf *vbo = buf; - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; - GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); - BMFace *efa; +#include "draw_cache_extract.h" +#include "draw_cache_extract_mesh_private.h" +#include "draw_cache_inline.h" - /* Quicker than doing it for each loop. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - for (int f = 0; f < mr->poly_len; f++) { - efa = BM_face_at_index(mr->bm, f); - const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN); - if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[f] == ORIGINDEX_NONE)) { - nor[f] = GPU_normal_convert_i10_v3(invalid_normal); - nor[f].w = NOR_AND_FLAG_HIDDEN; - } - else { - nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa)); - /* Select / Active Flag. */ - nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? - ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : - NOR_AND_FLAG_DEFAULT); - } - } - } - else { - for (int f = 0; f < mr->poly_len; f++) { - efa = bm_original_face_get(mr, f); - const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN); - if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[f] == ORIGINDEX_NONE)) { - nor[f] = GPU_normal_convert_i10_v3(invalid_normal); - nor[f].w = NOR_AND_FLAG_HIDDEN; - } - else { - nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa)); - /* Select / Active Flag. */ - nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? - ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : - NOR_AND_FLAG_DEFAULT); - } - } - } -} +// #define DEBUG_TIME -static const MeshExtract extract_fdots_nor = { - .init = extract_fdots_nor_init, - .finish = extract_fdots_nor_finish, - .data_flag = MR_DATA_POLY_NOR, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#endif -/** \} */ +#define CHUNK_SIZE 8192 /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots High Quality Normal and edit flag +/** \name Mesh Elements Extract Struct * \{ */ -static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); +typedef struct MeshExtractRunData { + const MeshExtract *extractor; + void *buffer; + void *user_data; +} MeshExtractRunData; - return NULL; -} +typedef struct MeshExtractRunDataArray { + int len; + MeshExtractRunData items[M_EXTRACT_LEN]; +} MeshExtractRunDataArray; -static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf, - void *UNUSED(data)) +static void mesh_extract_run_data_array_init(MeshExtractRunDataArray *array) { - GPUVertBuf *vbo = buf; - static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; - short *nor = (short *)GPU_vertbuf_get_data(vbo); - BMFace *efa; - - /* Quicker than doing it for each loop. */ - if (mr->extract_type == MR_EXTRACT_BMESH) { - for (int f = 0; f < mr->poly_len; f++) { - efa = BM_face_at_index(mr->bm, f); - const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN); - if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[f] == ORIGINDEX_NONE)) { - normal_float_to_short_v3(&nor[f * 4], invalid_normal); - nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN; - } - else { - normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa)); - /* Select / Active Flag. */ - nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? - ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : - NOR_AND_FLAG_DEFAULT); - } - } - } - else { - for (int f = 0; f < mr->poly_len; f++) { - efa = bm_original_face_get(mr, f); - const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN); - if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[f] == ORIGINDEX_NONE)) { - normal_float_to_short_v3(&nor[f * 4], invalid_normal); - nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN; - } - else { - normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa)); - /* Select / Active Flag. */ - nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? - ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : - NOR_AND_FLAG_DEFAULT); - } - } - } + array->len = 0; } -static const MeshExtract extract_fdots_nor_hq = { - .init = extract_fdots_nor_hq_init, - .finish = extract_fdots_nor_hq_finish, - .data_flag = MR_DATA_POLY_NOR, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots UV - * \{ */ - -typedef struct MeshExtract_FdotUV_Data { - float (*vbo_data)[2]; - MLoopUV *uv_data; - int cd_ofs; -} MeshExtract_FdotUV_Data; - -static void *extract_fdots_uv_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void mesh_extract_run_data_array_add_ex(MeshExtractRunDataArray *array, + const MeshExtractRunData *run_data) { - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_vertformat_alias_add(&format, "au"); - GPU_vertformat_alias_add(&format, "pos"); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); - - if (!mr->use_subsurf_fdots) { - /* Clear so we can accumulate on it. */ - memset(GPU_vertbuf_get_data(vbo), 0x0, mr->poly_len * GPU_vertbuf_get_format(vbo)->stride); - } - - MeshExtract_FdotUV_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (float(*)[2])GPU_vertbuf_get_data(vbo); - - if (mr->extract_type == MR_EXTRACT_BMESH) { - data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); - } - else { - data->uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); - } - return data; + array->items[array->len] = *run_data; + array->len++; } -static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *_data) +static void mesh_extract_run_data_array_add(MeshExtractRunDataArray *array, + const MeshExtract *extractor) { - MeshExtract_FdotUV_Data *data = _data; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - float w = 1.0f / (float)f->len; - const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); - madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w); - } while ((l_iter = l_iter->next) != l_first); + MeshExtractRunData run_data; + run_data.extractor = extractor; + run_data.buffer = NULL; + run_data.user_data = NULL; + mesh_extract_run_data_array_add_ex(array, &run_data); } -static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *_data) +static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray *src, + MeshExtractRunDataArray *dst, + eMRIterType iter_type) { - MeshExtract_FdotUV_Data *data = _data; - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - if (mr->use_subsurf_fdots) { - const MVert *mv = &mr->mvert[ml->v]; - if (mv->flag & ME_VERT_FACEDOT) { - copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv); - } + for (int i = 0; i < src->len; i++) { + + const MeshExtractRunData *data = &src->items[i]; + const MeshExtract *extractor = data->extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; } - else { - float w = 1.0f / (float)mp->totloop; - madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w); + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + mesh_extract_run_data_array_add_ex(dst, data); + continue; } } } -static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_fdots_uv = { - .init = extract_fdots_uv_init, - .iter_poly_bm = extract_fdots_uv_iter_poly_bm, - .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh, - .finish = extract_fdots_uv_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Edit UV flag - * \{ */ - -typedef struct MeshExtract_EditUVFdotData_Data { - EditLoopData *vbo_data; - int cd_ofs; -} MeshExtract_EditUVFdotData_Data; - -static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); - - MeshExtract_EditUVFdotData_Data *data = MEM_callocN(sizeof(*data), __func__); - data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); - data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); - return data; -} - -static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr, - BMFace *f, - const int UNUSED(f_index), - void *_data) -{ - MeshExtract_EditUVFdotData_Data *data = _data; - EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; - memset(eldata, 0x0, sizeof(*eldata)); - mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); -} - -static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *UNUSED(mp), - const int mp_index, - void *_data) -{ - MeshExtract_EditUVFdotData_Data *data = _data; - EditLoopData *eldata = &data->vbo_data[mp_index]; - memset(eldata, 0x0, sizeof(*eldata)); - BMFace *efa = bm_original_face_get(mr, mp_index); - if (efa) { - mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); - } -} - -static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), - void *UNUSED(buf), - void *data) -{ - MEM_freeN(data); -} - -static const MeshExtract extract_fdots_edituv_data = { - .init = extract_fdots_edituv_data_init, - .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm, - .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh, - .finish = extract_fdots_edituv_data_finish, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Skin Modifier Roots - * \{ */ - -typedef struct SkinRootData { - float size; - float local_pos[3]; -} SkinRootData; - -static void *extract_skin_roots_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) +static void mesh_extract_run_data_array_filter_threading( + const MeshExtractRunDataArray *src, MeshExtractRunDataArray *dst_multi_threaded) { - GPUVertBuf *vbo = buf; - /* Exclusively for edit mode. */ - BLI_assert(mr->bm); - - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->bm->totvert); - - SkinRootData *vbo_data = (SkinRootData *)GPU_vertbuf_get_data(vbo); - - int root_len = 0; - int cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MVERT_SKIN); - - BMIter iter; - BMVert *eve; - BM_ITER_MESH (eve, &iter, mr->bm, BM_VERTS_OF_MESH) { - const MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_ofs); - if (vs->flag & MVERT_SKIN_ROOT) { - vbo_data->size = (vs->radius[0] + vs->radius[1]) * 0.5f; - copy_v3_v3(vbo_data->local_pos, bm_vert_co_get(mr, eve)); - vbo_data++; - root_len++; + for (int i = 0; i < src->len; i++) { + const MeshExtract *extractor = src->items[i].extractor; + if (extractor->use_threading) { + mesh_extract_run_data_array_add(dst_multi_threaded, extractor); } } - - /* It's really unlikely that all verts will be roots. Resize to avoid losing VRAM. */ - GPU_vertbuf_data_len_set(vbo, root_len); - - return NULL; } -static const MeshExtract extract_skin_roots = { - .init = extract_skin_roots_init, - .data_flag = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; - /** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Extract Selection Index - * \{ */ - -static void *extract_select_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* TODO rename "color" to something more descriptive. */ - GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); - } - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); - return GPU_vertbuf_get_data(vbo); -} - -/* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the - * select element associated with this loop ID. This would remove the need for this separate - * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the - * shader to output original index. */ - -static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int f_index, - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - ((uint32_t *)data)[l_index] = f_index; - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->e); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *f, - const int UNUSED(f_index), - void *data) -{ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->v); - } while ((l_iter = l_iter->next) != l_first); -} - -static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *data) -{ - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); -} - -static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr, - BMEdge *eed, - const int ledge_index, - void *data) -{ - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); -} - -static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr, - BMVert *eve, - const int lvert_index, - void *data) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); -} - -static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int mp_index, - void *data) -{ - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; - } -} - -static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; - } -} - -static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *mp, - const int UNUSED(mp_index), - void *data) -{ - const MLoop *mloop = mr->mloop; - const int ml_index_end = mp->loopstart + mp->totloop; - for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { - const MLoop *ml = &mloop[ml_index]; - ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; - } -} - -static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *UNUSED(med), - const uint ledge_index, - void *data) -{ - const int e_index = mr->ledges[ledge_index]; - const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; -} - -static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, - const MEdge *med, - const uint ledge_index, - void *data) -{ - int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; - int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; - ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; -} - -static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, - const MVert *UNUSED(mv), - const int lvert_index, - void *data) -{ - const int offset = mr->loop_len + (mr->edge_loose_len * 2); - - const int v_index = mr->lverts[lvert_index]; - const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; - ((uint32_t *)data)[offset + lvert_index] = v_orig; -} - -static const MeshExtract extract_poly_idx = { - .init = extract_select_idx_init, - .iter_poly_bm = extract_poly_idx_iter_poly_bm, - .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; - -static const MeshExtract extract_edge_idx = { - .init = extract_select_idx_init, - .iter_poly_bm = extract_edge_idx_iter_poly_bm, - .iter_poly_mesh = extract_edge_idx_iter_poly_mesh, - .iter_ledge_bm = extract_edge_idx_iter_ledge_bm, - .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; - -static const MeshExtract extract_vert_idx = { - .init = extract_select_idx_init, - .iter_poly_bm = extract_vert_idx_iter_poly_bm, - .iter_poly_mesh = extract_vert_idx_iter_poly_mesh, - .iter_ledge_bm = extract_vert_idx_iter_ledge_bm, - .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh, - .iter_lvert_bm = extract_vert_idx_iter_lvert_bm, - .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; - -static void *extract_fdot_idx_init(const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buf) -{ - GPUVertBuf *vbo = buf; - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* TODO rename "color" to something more descriptive. */ - GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); - } - - GPU_vertbuf_init_with_format(vbo, &format); - GPU_vertbuf_data_alloc(vbo, mr->poly_len); - return GPU_vertbuf_get_data(vbo); -} - -static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), - BMFace *UNUSED(f), - const int f_index, - void *data) -{ - ((uint32_t *)data)[f_index] = f_index; -} - -static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, - const MPoly *UNUSED(mp), - const int mp_index, - void *data) -{ - if (mr->p_origindex != NULL) { - ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; - } - else { - ((uint32_t *)data)[mp_index] = mp_index; - } -} - -static const MeshExtract extract_fdot_idx = { - .init = extract_fdot_idx_init, - .iter_poly_bm = extract_fdot_idx_iter_poly_bm, - .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, - .data_flag = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; - /* ---------------------------------------------------------------------- */ /** \name Extract * \{ */ @@ -6088,52 +664,6 @@ static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_g /** \} */ -/* ---------------------------------------------------------------------- */ -/** \name Override extractors - * Extractors can be overridden. When overridden a specialized version is used. The next functions - * would check for any needed overrides and usage of the specialized version. - * \{ */ - -static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor) -{ - if (extractor == &extract_pos_nor) { - return &extract_pos_nor_hq; - } - if (extractor == &extract_lnor) { - return &extract_lnor_hq; - } - if (extractor == &extract_tan) { - return &extract_tan_hq; - } - if (extractor == &extract_fdots_nor) { - return &extract_fdots_nor_hq; - } - return extractor; -} - -static const MeshExtract *mesh_extract_override_loose_lines(const MeshExtract *extractor) -{ - if (extractor == &extract_lines) { - return &extract_lines_with_lines_loose; - } - return extractor; -} - -static const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, - const bool do_hq_normals, - const bool do_lines_loose_subbuffer) -{ - if (do_hq_normals) { - extractor = mesh_extract_override_hq_normals(extractor); - } - if (do_lines_loose_subbuffer) { - extractor = mesh_extract_override_loose_lines(extractor); - } - return extractor; -} - -/** \} */ - /* ---------------------------------------------------------------------- */ /** \name Extract Loop * \{ */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c new file mode 100644 index 00000000000..cc787b5a40f --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -0,0 +1,4748 @@ +/* + * 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 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#include "MEM_guardedalloc.h" + +#include "atomic_ops.h" + +#include "DNA_object_types.h" + +#include "BLI_edgehash.h" +#include "BLI_jitter_2d.h" +#include "BLI_kdopbvh.h" +#include "BLI_string.h" + +#include "BKE_bvhutils.h" +#include "BKE_deform.h" +#include "BKE_editmesh.h" +#include "BKE_editmesh_bvh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_editmesh_tangent.h" +#include "BKE_mesh.h" +#include "BKE_mesh_tangent.h" +#include "BKE_paint.h" + +#include "ED_uvedit.h" + +#include "GPU_capabilities.h" + +#include "draw_cache_extract_mesh_private.h" +#include "draw_cache_impl.h" + +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) +{ + /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to + * `MeshBufferCache *`. What shows a different usage versus intent. */ + void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset); + void *buffer = *buffer_ptr; + BLI_assert(buffer); + return buffer; +} + +eMRIterType mesh_extract_iter_type(const MeshExtract *ext) +{ + eMRIterType type = 0; + SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI); + SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY); + SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE); + SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT); + return type; +} + +/* ---------------------------------------------------------------------- */ +/** \name Override extractors + * Extractors can be overridden. When overridden a specialized version is used. The next functions + * would check for any needed overrides and usage of the specialized version. + * \{ */ + +static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor) +{ + if (extractor == &extract_pos_nor) { + return &extract_pos_nor_hq; + } + if (extractor == &extract_lnor) { + return &extract_lnor_hq; + } + if (extractor == &extract_tan) { + return &extract_tan_hq; + } + if (extractor == &extract_fdots_nor) { + return &extract_fdots_nor_hq; + } + return extractor; +} + +static const MeshExtract *mesh_extract_override_loose_lines(const MeshExtract *extractor) +{ + if (extractor == &extract_lines) { + return &extract_lines_with_lines_loose; + } + return extractor; +} + +const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals, + const bool do_lines_loose_subbuffer) +{ + if (do_hq_normals) { + extractor = mesh_extract_override_hq_normals(extractor); + } + if (do_lines_loose_subbuffer) { + extractor = mesh_extract_override_loose_lines(extractor); + } + return extractor; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Triangles Indices + * \{ */ + +typedef struct MeshExtract_Tri_Data { + GPUIndexBufBuilder elb; + int *tri_mat_start; + int *tri_mat_end; +} MeshExtract_Tri_Data; + +static void *extract_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__); + + size_t mat_tri_idx_size = sizeof(int) * mr->mat_len; + data->tri_mat_start = MEM_callocN(mat_tri_idx_size, __func__); + data->tri_mat_end = MEM_callocN(mat_tri_idx_size, __func__); + + int *mat_tri_len = data->tri_mat_start; + /* Count how many triangle for each material. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMFace *efa; + BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + int mat = min_ii(efa->mat_nr, mr->mat_len - 1); + mat_tri_len[mat] += efa->len - 2; + } + } + } + else { + const MPoly *mp = mr->mpoly; + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + int mat = min_ii(mp->mat_nr, mr->mat_len - 1); + mat_tri_len[mat] += mp->totloop - 2; + } + } + } + /* Accumulate triangle lengths per material to have correct offsets. */ + int ofs = mat_tri_len[0]; + mat_tri_len[0] = 0; + for (int i = 1; i < mr->mat_len; i++) { + int tmp = mat_tri_len[i]; + mat_tri_len[i] = ofs; + ofs += tmp; + } + + memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size); + + int visible_tri_tot = ofs; + GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len); + + return data; +} + +static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, + BMLoop **elt, + const int UNUSED(elt_index), + void *_data) +{ + MeshExtract_Tri_Data *data = _data; + const int mat_last = mr->mat_len - 1; + + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(elt[0]->f->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts(&data->elb, + mat_tri_ofs[mat]++, + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); + } +} + +static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, + const MLoopTri *mlt, + const int UNUSED(elt_index), + void *_data) +{ + MeshExtract_Tri_Data *data = _data; + const int mat_last = mr->mat_len - 1; + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + int *mat_tri_ofs = data->tri_mat_end; + const int mat = min_ii(mp->mat_nr, mat_last); + GPU_indexbuf_set_tri_verts( + &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); + } +} + +static void extract_tris_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *_data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_Tri_Data *data = _data; + GPU_indexbuf_build_in_place(&data->elb, ibo); + + /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch + * is created before the surfaces-per-material. */ + if (mr->use_final_mesh && cache->final.tris_per_mat) { + MeshBufferCache *mbc_final = &cache->final; + for (int i = 0; i < mr->mat_len; i++) { + /* These IBOs have not been queried yet but we create them just in case they are needed + * later since they are not tracked by mesh_buffer_cache_create_requested(). */ + if (mbc_final->tris_per_mat[i] == NULL) { + mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); + } + /* Multiply by 3 because these are triangle indices. */ + const int mat_start = data->tri_mat_start[i]; + const int mat_end = data->tri_mat_end[i]; + const int start = mat_start * 3; + const int len = (mat_end - mat_start) * 3; + GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); + } + } + MEM_freeN(data->tri_mat_start); + MEM_freeN(data->tri_mat_end); + MEM_freeN(data); +} + +const MeshExtract extract_tris = {.init = extract_tris_init, + .iter_looptri_bm = extract_tris_iter_looptri_bm, + .iter_looptri_mesh = extract_tris_iter_looptri_mesh, + .finish = extract_tris_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edges Indices + * \{ */ + +static void *extract_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); + /* Put loose edges at the end. */ + GPU_indexbuf_init( + elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len); + return elb; +} + +static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *elb) +{ + BMLoop *l_iter, *l_first; + /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev; + do { + if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_line_verts(elb, + BM_elem_index_get(l_iter->e), + BM_elem_index_get(l_iter), + BM_elem_index_get(l_iter->next)); + } + else { + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e)); + } + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *elb) +{ + /* Using poly & loop iterator would complicate accessing the adjacent loop. */ + const MLoop *mloop = mr->mloop; + const MEdge *medge = mr->medge; + if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) { + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + const MEdge *med = &medge[ml->e]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } + else { + GPU_indexbuf_set_line_restart(elb, ml->e); + } + } while ((ml_index = ml_index_next++) != ml_index_last); + } + else { + const int ml_index_last = mp->loopstart + (mp->totloop - 1); + int ml_index = ml_index_last, ml_index_next = mp->loopstart; + do { + const MLoop *ml = &mloop[ml_index]; + GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); + } while ((ml_index = ml_index_next++) != ml_index_last); + } +} + +static void extract_lines_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *elb) +{ + const int l_index_offset = mr->edge_len + ledge_index; + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); + } + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed)); +} + +static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *elb) +{ + const int l_index_offset = mr->edge_len + ledge_index; + const int e_index = mr->ledges[ledge_index]; + if (!((mr->use_hide && (med->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { + const int l_index = mr->loop_len + ledge_index * 2; + GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1); + } + else { + GPU_indexbuf_set_line_restart(elb, l_index_offset); + } + /* Don't render the edge twice. */ + GPU_indexbuf_set_line_restart(elb, e_index); +} + +static void extract_lines_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *elb) +{ + GPUIndexBuf *ibo = buf; + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); +} + +const MeshExtract extract_lines = {.init = extract_lines_init, + .iter_poly_bm = extract_lines_iter_poly_bm, + .iter_poly_mesh = extract_lines_iter_poly_mesh, + .iter_ledge_bm = extract_lines_iter_ledge_bm, + .iter_ledge_mesh = extract_lines_iter_ledge_mesh, + .finish = extract_lines_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Loose Edges Sub Buffer + * \{ */ + +static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache) +{ + BLI_assert(cache->final.ibo.lines); + /* Multiply by 2 because these are edges indices. */ + const int start = mr->edge_len * 2; + const int len = mr->edge_loose_len * 2; + GPU_indexbuf_create_subrange_in_place( + cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len); + cache->no_loose_wire = (len == 0); +} + +static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *elb) +{ + GPUIndexBuf *ibo = buf; + GPU_indexbuf_build_in_place(elb, ibo); + extract_lines_loose_subbuffer(mr, cache); + MEM_freeN(elb); +} + +const MeshExtract extract_lines_with_lines_loose = { + .init = extract_lines_init, + .iter_poly_bm = extract_lines_iter_poly_bm, + .iter_poly_mesh = extract_lines_iter_poly_mesh, + .iter_ledge_bm = extract_lines_iter_ledge_bm, + .iter_ledge_mesh = extract_lines_iter_ledge_mesh, + .finish = extract_lines_with_lines_loose_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Point Indices + * \{ */ + +static void *extract_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); + GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len); + return elb; +} + +BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, BMVert *eve, int l_index) +{ + const int v_index = BM_elem_index_get(eve); + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, v_index, l_index); + } + else { + GPU_indexbuf_set_point_restart(elb, v_index); + } +} + +BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, + const MeshRenderData *mr, + const int v_index, + const int l_index) +{ + const MVert *mv = &mr->mvert[v_index]; + if (!((mr->use_hide && (mv->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && + (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) { + GPU_indexbuf_set_point_vert(elb, v_index, l_index); + } + else { + GPU_indexbuf_set_point_restart(elb, v_index); + } +} + +static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *elb) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + vert_set_bm(elb, l_iter->v, l_index); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_points_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *elb) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + vert_set_mesh(elb, mr, ml->v, ml_index); + } +} + +static void extract_points_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *elb) +{ + vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2)); + vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1); +} + +static void extract_points_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *elb) +{ + vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2)); + vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1); +} + +static void extract_points_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *elb) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + vert_set_bm(elb, eve, offset + lvert_index); +} + +static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *UNUSED(mv), + const int lvert_index, + void *elb) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index); +} + +static void extract_points_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *elb) +{ + GPUIndexBuf *ibo = buf; + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); +} + +const MeshExtract extract_points = {.init = extract_points_init, + .iter_poly_bm = extract_points_iter_poly_bm, + .iter_poly_mesh = extract_points_iter_poly_mesh, + .iter_ledge_bm = extract_points_iter_ledge_bm, + .iter_ledge_mesh = extract_points_iter_ledge_mesh, + .iter_lvert_bm = extract_points_iter_lvert_bm, + .iter_lvert_mesh = extract_points_iter_lvert_mesh, + .finish = extract_points_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Indices + * \{ */ + +static void *extract_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); + GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); + return elb; +} + +static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, + void *elb) +{ + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + GPU_indexbuf_set_point_vert(elb, f_index, f_index); + } + else { + GPU_indexbuf_set_point_restart(elb, f_index); + } +} + +static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *elb) +{ + if (mr->use_subsurf_fdots) { + /* Check #ME_VERT_FACEDOT. */ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + const MVert *mv = &mr->mvert[ml->v]; + if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + return; + } + } + GPU_indexbuf_set_point_restart(elb, mp_index); + } + else { + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); + } + else { + GPU_indexbuf_set_point_restart(elb, mp_index); + } + } +} + +static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *elb) +{ + GPUIndexBuf *ibo = buf; + GPU_indexbuf_build_in_place(elb, ibo); + MEM_freeN(elb); +} + +const MeshExtract extract_fdots = {.init = extract_fdots_init, + .iter_poly_bm = extract_fdots_iter_poly_bm, + .iter_poly_mesh = extract_fdots_iter_poly_mesh, + .finish = extract_fdots_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Paint Mask Line Indices + * \{ */ + +typedef struct MeshExtract_LinePaintMask_Data { + GPUIndexBufBuilder elb; + /** One bit per edge set if face is selected. */ + BLI_bitmap select_map[0]; +} MeshExtract_LinePaintMask_Data; + +static void *extract_lines_paint_mask_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); + MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len); + return data; +} + +static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_LinePaintMask_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const int e_index = ml->e; + const MEdge *me = &mr->medge[e_index]; + if (!((mr->use_hide && (me->flag & ME_HIDE)) || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && + (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { + + const int ml_index_last = mp->totloop + mp->loopstart - 1; + const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); + if (mp->flag & ME_FACE_SEL) { + if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) { + /* Hide edge as it has more than 2 selected loop. */ + GPU_indexbuf_set_line_restart(&data->elb, e_index); + } + else { + /* First selected loop. Set edge visible, overwriting any unselected loop. */ + GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); + } + } + else { + /* Set these unselected loop only if this edge has no other selected loop. */ + if (!BLI_BITMAP_TEST(data->select_map, e_index)) { + GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other); + } + } + } + else { + GPU_indexbuf_set_line_restart(&data->elb, e_index); + } + } +} + +static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_LinePaintMask_Data *data = _data; + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +const MeshExtract extract_lines_paint_mask = { + .init = extract_lines_paint_mask_init, + .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh, + .finish = extract_lines_paint_mask_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Line Adjacency Indices + * \{ */ + +#define NO_EDGE INT_MAX + +typedef struct MeshExtract_LineAdjacency_Data { + GPUIndexBufBuilder elb; + EdgeHash *eh; + bool is_manifold; + /* Array to convert vert index to any loop index of this vert. */ + uint vert_to_loop[0]; +} MeshExtract_LineAdjacency_Data; + +static void *extract_lines_adjacency_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) +{ + /* Similar to poly_to_tri_count(). + * There is always (loop + triangle - 1) edges inside a polygon. + * Accumulate for all polys and you get : */ + uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len; + + size_t vert_to_loop_size = sizeof(uint) * mr->vert_len; + + MeshExtract_LineAdjacency_Data *data = MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len); + data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len); + data->is_manifold = true; + return data; +} + +BLI_INLINE void lines_adjacency_triangle( + uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data) +{ + GPUIndexBufBuilder *elb = &data->elb; + /* Iterate around the triangle's edges. */ + for (int e = 0; e < 3; e++) { + SHIFT3(uint, v3, v2, v1); + SHIFT3(uint, l3, l2, l1); + + bool inv_indices = (v2 > v3); + void **pval; + bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval); + int v_data = POINTER_AS_INT(*pval); + if (!value_is_init || v_data == NO_EDGE) { + /* Save the winding order inside the sign bit. Because the + * Edge-hash sort the keys and we need to compare winding later. */ + int value = (int)l1 + 1; /* 0 cannot be signed so add one. */ + *pval = POINTER_FROM_INT((inv_indices) ? -value : value); + /* Store loop indices for remaining non-manifold edges. */ + data->vert_to_loop[v2] = l2; + data->vert_to_loop[v3] = l3; + } + else { + /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */ + *pval = POINTER_FROM_INT(NO_EDGE); + bool inv_opposite = (v_data < 0); + uint l_opposite = (uint)abs(v_data) - 1; + /* TODO Make this part thread-safe. */ + if (inv_opposite == inv_indices) { + /* Don't share edge if triangles have non matching winding. */ + GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1); + GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite); + data->is_manifold = false; + } + else { + GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite); + } + } + } +} + +static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr), + BMLoop **elt, + const int UNUSED(elt_index), + void *data) +{ + if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { + lines_adjacency_triangle(BM_elem_index_get(elt[0]->v), + BM_elem_index_get(elt[1]->v), + BM_elem_index_get(elt[2]->v), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2]), + data); + } +} + +static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, + const MLoopTri *mlt, + const int UNUSED(elt_index), + void *data) +{ + const MPoly *mp = &mr->mpoly[mlt->poly]; + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); + } +} + +static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *cache, + void *buf, + void *_data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_LineAdjacency_Data *data = _data; + /* Create edges for remaining non manifold edges. */ + EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh); + for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + uint v2, v3, l1, l2, l3; + int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi)); + if (v_data != NO_EDGE) { + BLI_edgehashIterator_getKey(ehi, &v2, &v3); + l1 = (uint)abs(v_data) - 1; + if (v_data < 0) { /* inv_opposite */ + SWAP(uint, v2, v3); + } + l2 = data->vert_to_loop[v2]; + l3 = data->vert_to_loop[v3]; + GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1); + data->is_manifold = false; + } + } + BLI_edgehashIterator_free(ehi); + BLI_edgehash_free(data->eh, NULL); + + cache->is_manifold = data->is_manifold; + + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +#undef NO_EDGE + +const MeshExtract extract_lines_adjacency = { + .init = extract_lines_adjacency_init, + .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm, + .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh, + .finish = extract_lines_adjacency_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Triangles Indices + * \{ */ + +typedef struct MeshExtract_EditUvElem_Data { + GPUIndexBufBuilder elb; + bool sync_selection; +} MeshExtract_EditUvElem_Data; + +static void *extract_edituv_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_tri_add( + MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3); + } +} + +static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr), + BMLoop **elt, + const int UNUSED(elt_index), + void *data) +{ + edituv_tri_add(data, + BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN), + BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT), + BM_elem_index_get(elt[0]), + BM_elem_index_get(elt[1]), + BM_elem_index_get(elt[2])); +} + +static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, + const MLoopTri *mlt, + const int UNUSED(elt_index), + void *data) +{ + const MPoly *mp = &mr->mpoly[mlt->poly]; + edituv_tri_add(data, + (mp->flag & ME_HIDE) != 0, + (mp->flag & ME_FACE_SEL) != 0, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2]); +} + +static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_EditUvElem_Data *extract_data = data; + GPU_indexbuf_build_in_place(&extract_data->elb, ibo); + MEM_freeN(extract_data); +} + +const MeshExtract extract_edituv_tris = { + .init = extract_edituv_tris_init, + .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm, + .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh, + .finish = extract_edituv_tris_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Line Indices around faces + * \{ */ + +static void *extract_edituv_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); + + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_edge_add( + MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_line_verts(&data->elb, v1, v2); + } +} + +static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_edge_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + l_index, + BM_elem_index_get(l_iter->next)); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const int ml_index_last = mp->totloop + mp->loopstart - 1; + const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); + const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE); + edituv_edge_add(data, + (mp->flag & ME_HIDE) != 0 || !real_edge, + (mp->flag & ME_FACE_SEL) != 0, + ml_index, + ml_index_next); + } +} + +static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_EditUvElem_Data *extract_data = data; + GPU_indexbuf_build_in_place(&extract_data->elb, ibo); + MEM_freeN(extract_data); +} + +const MeshExtract extract_edituv_lines = { + .init = extract_edituv_lines_init, + .iter_poly_bm = extract_edituv_lines_iter_poly_bm, + .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh, + .finish = extract_edituv_lines_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Points Indices + * \{ */ + +static void *extract_edituv_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); + + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data, + bool hidden, + bool selected, + int v1) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_add_point_vert(&data->elb, v1); + } +} + +static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + edituv_point_add( + data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && + mr->v_origindex[ml->v] != ORIGINDEX_NONE); + edituv_point_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); + } +} + +static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_EditUvElem_Data *extract_data = data; + GPU_indexbuf_build_in_place(&extract_data->elb, ibo); + MEM_freeN(extract_data); +} + +const MeshExtract extract_edituv_points = { + .init = extract_edituv_points_init, + .iter_poly_bm = extract_edituv_points_iter_poly_bm, + .iter_poly_mesh = extract_edituv_points_iter_poly_mesh, + .finish = extract_edituv_points_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Facedots Indices + * \{ */ + +static void *extract_edituv_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) +{ + MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); + GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); + + data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0; + return data; +} + +BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data, + bool hidden, + bool selected, + int face_index) +{ + if (!hidden && (data->sync_selection || selected)) { + GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index); + } + else { + GPU_indexbuf_set_point_restart(&data->elb, face_index); + } +} + +static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, + void *data) +{ + edituv_facedot_add(data, + BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN), + BM_elem_flag_test_bool(f, BM_ELEM_SELECT), + f_index); +} + +static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + if (mr->use_subsurf_fdots) { + /* Check #ME_VERT_FACEDOT. */ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + const bool subd_fdot = (!mr->use_subsurf_fdots || + (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0); + edituv_facedot_add(data, + ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, + (mp->flag & ME_FACE_SEL) != 0, + mp_index); + } + } + else { + const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[mp_index] != ORIGINDEX_NONE); + edituv_facedot_add( + data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); + } +} + +static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + GPUIndexBuf *ibo = buf; + MeshExtract_EditUvElem_Data *data = _data; + GPU_indexbuf_build_in_place(&data->elb, ibo); + MEM_freeN(data); +} + +const MeshExtract extract_edituv_fdots = { + .init = extract_edituv_fdots_init, + .iter_poly_bm = extract_edituv_fdots_iter_poly_bm, + .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh, + .finish = extract_edituv_fdots_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Position and Vertex Normal + * \{ */ + +typedef struct PosNorLoop { + float pos[3]; + GPUPackedNormal nor; +} PosNorLoop; + +typedef struct MeshExtract_PosNor_Data { + PosNorLoop *vbo_data; + GPUNormal normals[]; +} MeshExtract_PosNor_Data; + +static void *extract_pos_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING Adjust #PosNorLoop struct accordingly. */ + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "vnor"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + /* Pack normals per vert, reduce amount of computation. */ + size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; + MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); + data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo); + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMVert *eve; + int v; + BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) { + data->normals[v].low = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve)); + } + } + else { + const MVert *mv = mr->mvert; + for (int v = 0; v < mr->vert_len; v++, mv++) { + data->normals[v].low = GPU_normal_convert_i10_s3(mv->no); + } + } + return data; +} + +static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low; + vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + PosNorLoop *vert = &data->vbo_data[ml_index]; + const MVert *mv = &mr->mvert[ml->v]; + copy_v3_v3(vert->pos, mv->co); + vert->nor = data->normals[ml->v].low; + /* Flag for paint mode overlay. */ + if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && + (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { + vert->nor.w = -1; + } + else if (mv->flag & SELECT) { + vert->nor.w = 1; + } + else { + vert->nor.w = 0; + } + } +} + +static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + + int l_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low; + vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low; +} + +static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + vert[0].nor = data->normals[med->v1].low; + vert[1].nor = data->normals[med->v2].low; +} + +static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int l_index = offset + lvert_index; + PosNorLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + vert->nor = data->normals[BM_elem_index_get(eve)].low; +} + +static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *mv, + const int lvert_index, + void *_data) +{ + MeshExtract_PosNor_Data *data = _data; + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + vert->nor = data->normals[v_index].low; +} + +static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_pos_nor = {.init = extract_pos_nor_init, + .iter_poly_bm = extract_pos_nor_iter_poly_bm, + .iter_poly_mesh = extract_pos_nor_iter_poly_mesh, + .iter_ledge_bm = extract_pos_nor_iter_ledge_bm, + .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh, + .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, + .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, + .finish = extract_pos_nor_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Position and High Quality Vertex Normal + * \{ */ + +typedef struct PosNorHQLoop { + float pos[3]; + short nor[4]; +} PosNorHQLoop; + +typedef struct MeshExtract_PosNorHQ_Data { + PosNorHQLoop *vbo_data; + GPUNormal normals[]; +} MeshExtract_PosNorHQ_Data; + +static void *extract_pos_nor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING Adjust #PosNorHQLoop struct accordingly. */ + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "vnor"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + /* Pack normals per vert, reduce amount of computation. */ + size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len; + MeshExtract_PosNorHQ_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__); + data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo); + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMVert *eve; + int v; + BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) { + normal_float_to_short_v3(data->normals[v].high, bm_vert_no_get(mr, eve)); + } + } + else { + const MVert *mv = mr->mvert; + for (int v = 0; v < mr->vert_len; v++, mv++) { + copy_v3_v3_short(data->normals[v].high, mv->no); + } + } + return data; +} + +static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high); + + BMFace *efa = l_iter->f; + vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + const MVert *mv = &mr->mvert[ml->v]; + copy_v3_v3(vert->pos, mv->co); + copy_v3_v3_short(vert->nor, data->normals[ml->v].high); + + /* Flag for paint mode overlay. */ + if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || + ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && + (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { + vert->nor[3] = -1; + } + else if (mv->flag & SELECT) { + vert->nor[3] = 1; + } + else { + vert->nor[3] = 0; + } + } +} + +static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + int l_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1)); + copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2)); + copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high); + vert[1].nor[3] = 0; +} + +static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + const int ml_index = mr->loop_len + ledge_index * 2; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co); + copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co); + copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high); + vert[0].nor[3] = 0; + copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high); + vert[1].nor[3] = 0; +} + +static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int l_index = offset + lvert_index; + PosNorHQLoop *vert = &data->vbo_data[l_index]; + copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve)); + copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high); + vert->nor[3] = 0; +} + +static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *mv, + const int lvert_index, + void *_data) +{ + MeshExtract_PosNorHQ_Data *data = _data; + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int ml_index = offset + lvert_index; + const int v_index = mr->lverts[lvert_index]; + PosNorHQLoop *vert = &data->vbo_data[ml_index]; + copy_v3_v3(vert->pos, mv->co); + copy_v3_v3_short(vert->nor, data->normals[v_index].high); + vert->nor[3] = 0; +} + +static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_pos_nor_hq = { + .init = extract_pos_nor_hq_init, + .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm, + .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh, + .iter_ledge_bm = extract_pos_nor_hq_iter_ledge_bm, + .iter_ledge_mesh = extract_pos_nor_hq_iter_ledge_mesh, + .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm, + .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh, + .finish = extract_pos_nor_hq_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; + +/** \} */ +/* ---------------------------------------------------------------------- */ +/** \name Extract HQ Loop Normal + * \{ */ + +typedef struct gpuHQNor { + short x, y, z, w; +} gpuHQNor; + +static void *extract_lnor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "lnor"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return GPU_vertbuf_get_data(vbo); +} + +static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]); + } + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l_iter->v)); + } + else { + normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f)); + } + } + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index]; + if (mr->loop_normals) { + normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]); + } + else if (mp->flag & ME_SMOOTH) { + copy_v3_v3_short(&lnor_data->x, mr->mvert[ml->v].no); + } + else { + normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[mp_index]); + } + + /* Flag for paint mode overlay. + * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. + * In paint mode it will use the un-mapped data to draw the wire-frame. */ + if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && + (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { + lnor_data->w = -1; + } + else if (mp->flag & ME_FACE_SEL) { + lnor_data->w = 1; + } + else { + lnor_data->w = 0; + } + } +} + +const MeshExtract extract_lnor_hq = {.init = extract_lnor_hq_init, + .iter_poly_bm = extract_lnor_hq_iter_poly_bm, + .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, + .data_flag = MR_DATA_LOOP_NOR, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; + +/** \} */ +/* ---------------------------------------------------------------------- */ +/** \name Extract Loop Normal + * \{ */ + +static void *extract_lnor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_alias_add(&format, "lnor"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return GPU_vertbuf_get_data(vbo); +} + +static void extract_lnor_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (mr->loop_normals) { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]); + } + else { + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3( + bm_vert_no_get(mr, l_iter->v)); + } + else { + ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f)); + } + } + ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0; + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index]; + if (mr->loop_normals) { + *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]); + } + else if (mp->flag & ME_SMOOTH) { + *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[ml->v].no); + } + else { + *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[mp_index]); + } + + /* Flag for paint mode overlay. + * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. + * In paint mode it will use the un-mapped data to draw the wire-frame. */ + if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && + (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { + lnor_data->w = -1; + } + else if (mp->flag & ME_FACE_SEL) { + lnor_data->w = 1; + } + else { + lnor_data->w = 0; + } + } +} + +const MeshExtract extract_lnor = {.init = extract_lnor_init, + .iter_poly_bm = extract_lnor_iter_poly_bm, + .iter_poly_mesh = extract_lnor_iter_poly_mesh, + .data_flag = MR_DATA_LOOP_NOR, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract UV layers + * \{ */ + +static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +{ + GPUVertBuf *vbo = buf; + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + uint32_t uv_layers = cache->cd_used.uv; + /* HACK to fix T68857 */ + if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) { + int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV); + if (layer != -1) { + uv_layers |= (1 << layer); + } + } + + for (int i = 0; i < MAX_MTFACE; i++) { + if (uv_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); + + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* UV layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "u%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* Auto layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + /* Active render layer name. */ + if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "u"); + } + /* Active display layer name. */ + if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "au"); + /* Alias to `pos` for edit uvs. */ + GPU_vertformat_alias_add(&format, "pos"); + } + /* Stencil mask uv layer name. */ + if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "mu"); + } + } + } + + int v_len = mr->loop_len; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + /* VBO will not be used, only allocate minimum of memory. */ + v_len = 1; + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, v_len); + + float(*uv_data)[2] = (float(*)[2])GPU_vertbuf_get_data(vbo); + for (int i = 0; i < MAX_MTFACE; i++) { + if (uv_layers & (1 << i)) { + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i); + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); + memcpy(uv_data, luv->uv, sizeof(*uv_data)); + uv_data++; + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) { + memcpy(uv_data, layer_data->uv, sizeof(*uv_data)); + } + } + } + } + + return NULL; +} + +const MeshExtract extract_uv = {.init = extract_uv_init, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Tangent layers + * \{ */ + +static void extract_tan_ex_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertBuf *vbo, + const bool do_hq) +{ + GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; + GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; + + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + uint32_t tan_layers = cache->cd_used.tan; + float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO); + bool orco_allocated = false; + const bool use_orco_tan = cache->cd_used.tan_orco != 0; + + int tan_len = 0; + char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; + + for (int i = 0; i < MAX_MTFACE; i++) { + if (tan_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Tangent layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); + /* Active render layer name. */ + if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "t"); + } + /* Active display layer name. */ + if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { + GPU_vertformat_alias_add(&format, "at"); + } + + BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME); + } + } + if (use_orco_tan && orco == NULL) { + /* If `orco` is not available compute it ourselves */ + orco_allocated = true; + orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMesh *bm = mr->bm; + for (int v = 0; v < mr->vert_len; v++) { + const BMVert *eve = BM_vert_at_index(bm, v); + /* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords. + * not the distorted ones. */ + copy_v3_v3(orco[v], eve->co); + } + } + else { + const MVert *mv = mr->mvert; + for (int v = 0; v < mr->vert_len; v++, mv++) { + copy_v3_v3(orco[v], mv->co); + } + } + BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0); + } + + /* Start Fresh */ + CustomData loop_data; + CustomData_reset(&loop_data); + if (tan_len != 0 || use_orco_tan) { + short tangent_mask = 0; + bool calc_active_tangent = false; + if (mr->extract_type == MR_EXTRACT_BMESH) { + BKE_editmesh_loop_tangent_calc(mr->edit_bmesh, + calc_active_tangent, + tangent_names, + tan_len, + mr->poly_normals, + mr->loop_normals, + orco, + &loop_data, + mr->loop_len, + &tangent_mask); + } + else { + BKE_mesh_calc_loop_tangent_ex(mr->mvert, + mr->mpoly, + mr->poly_len, + mr->mloop, + mr->mlooptri, + mr->tri_len, + cd_ldata, + calc_active_tangent, + tangent_names, + tan_len, + mr->poly_normals, + mr->loop_normals, + orco, + &loop_data, + mr->loop_len, + &tangent_mask); + } + } + + if (use_orco_tan) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); + GPU_vertformat_alias_add(&format, "t"); + GPU_vertformat_alias_add(&format, "at"); + } + + if (orco_allocated) { + MEM_SAFE_FREE(orco); + } + + int v_len = mr->loop_len; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + /* VBO will not be used, only allocate minimum of memory. */ + v_len = 1; + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, v_len); + + if (do_hq) { + short(*tan_data)[4] = (short(*)[4])GPU_vertbuf_get_data(vbo); + for (int i = 0; i < tan_len; i++) { + const char *name = tangent_names[i]; + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( + &loop_data, CD_TANGENT, name); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + normal_float_to_short_v3(*tan_data, layer_data[ml_index]); + (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; + tan_data++; + } + } + if (use_orco_tan) { + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + normal_float_to_short_v3(*tan_data, layer_data[ml_index]); + (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; + tan_data++; + } + } + } + else { + GPUPackedNormal *tan_data = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); + for (int i = 0; i < tan_len; i++) { + const char *name = tangent_names[i]; + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named( + &loop_data, CD_TANGENT, name); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]); + tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2; + tan_data++; + } + } + if (use_orco_tan) { + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]); + tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2; + tan_data++; + } + } + } + + CustomData_free(&loop_data, mr->loop_len); +} + +static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +{ + extract_tan_ex_init(mr, cache, buf, false); + return NULL; +} + +const MeshExtract extract_tan = {.init = extract_tan_init, + .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | + MR_DATA_LOOPTRI, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract HQ Tangent layers + * \{ */ + +static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +{ + extract_tan_ex_init(mr, cache, buf, true); + return NULL; +} + +const MeshExtract extract_tan_hq = { + .init = extract_tan_hq_init, + .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .use_threading = false, +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Sculpt Data + * \{ */ + +static void *extract_sculpt_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + GPUVertFormat format = {0}; + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; + + float *cd_mask = CustomData_get_layer(cd_vdata, CD_PAINT_MASK); + int *cd_face_set = CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); + + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "fset", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + typedef struct gpuSculptData { + uint8_t face_set_color[4]; + float mask; + } gpuSculptData; + + gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo); + MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK); + int cd_face_set_ofs = CustomData_get_offset(cd_pdata, CD_SCULPT_FACE_SETS); + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + float v_mask = 0.0f; + if (cd_mask) { + v_mask = BM_ELEM_CD_GET_FLOAT(l_iter->v, cd_mask_ofs); + } + vbo_data->mask = v_mask; + uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX}; + if (cd_face_set) { + const int face_set_id = BM_ELEM_CD_GET_INT(l_iter->f, cd_face_set_ofs); + if (face_set_id != mr->me->face_sets_color_default) { + BKE_paint_face_set_overlay_color_get( + face_set_id, mr->me->face_sets_color_seed, face_set_color); + } + } + copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color); + vbo_data++; + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + int mp_loop = 0; + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { + const MPoly *p = &mr->mpoly[mp_index]; + for (int l = 0; l < p->totloop; l++) { + float v_mask = 0.0f; + if (cd_mask) { + v_mask = cd_mask[loops[mp_loop].v]; + } + vbo_data->mask = v_mask; + + uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX}; + if (cd_face_set) { + const int face_set_id = cd_face_set[mp_index]; + /* Skip for the default color Face Set to render it white. */ + if (face_set_id != mr->me->face_sets_color_default) { + BKE_paint_face_set_overlay_color_get( + face_set_id, mr->me->face_sets_color_seed, face_set_color); + } + } + copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color); + mp_loop++; + vbo_data++; + } + } + } + + return NULL; +} + +const MeshExtract extract_sculpt_data = { + .init = extract_sculpt_data_init, + .data_flag = 0, + /* TODO: enable threading. */ + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract VCol + * \{ */ + +static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) +{ + GPUVertBuf *vbo = buf; + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + uint32_t vcol_layers = cache->cd_used.vcol; + uint32_t svcol_layers = cache->cd_used.sculpt_vcol; + + for (int i = 0; i < MAX_MCOL; i++) { + if (vcol_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + + BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) { + GPU_vertformat_alias_add(&format, "c"); + } + if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) { + GPU_vertformat_alias_add(&format, "ac"); + } + + /* Gather number of auto layers. */ + /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 && + CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + } + } + } + + /* Sculpt Vertex Colors */ + if (U.experimental.use_sculpt_vertex_colors) { + for (int i = 0; i < 8; i++) { + if (svcol_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + + BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { + GPU_vertformat_alias_add(&format, "c"); + } + if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { + GPU_vertformat_alias_add(&format, "ac"); + } + /* Gather number of auto layers. */ + /* We only do `vcols` that are not overridden by `uvs`. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + } + } + } + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + typedef struct gpuMeshVcol { + ushort r, g, b, a; + } gpuMeshVcol; + + gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); + MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP); + + for (int i = 0; i < MAX_MCOL; i++) { + if (vcol_layers & (1 << i)) { + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i); + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); + vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); + vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); + vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); + vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); + vcol_data++; + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) { + vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); + vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); + vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); + vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); + } + } + } + + if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) { + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs); + vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]); + vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]); + vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]); + vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]); + vcol_data++; + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) { + vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]); + vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]); + vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]); + vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]); + } + } + } + } + return NULL; +} + +const MeshExtract extract_vcol = {.init = extract_vcol_init, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Orco + * \{ */ + +typedef struct MeshExtract_Orco_Data { + float (*vbo_data)[4]; + float (*orco)[3]; +} MeshExtract_Orco_Data; + +static void *extract_orco_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex + * attributes. This is a substantial waste of video-ram and should be done another way. + * Unfortunately, at the time of writing, I did not found any other "non disruptive" + * alternative. */ + GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + CustomData *cd_vdata = &mr->me->vdata; + + MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__); + data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo); + data->orco = CustomData_get_layer(cd_vdata, CD_ORCO); + /* Make sure `orco` layer was requested only if needed! */ + BLI_assert(data->orco); + return data; +} + +static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + float *loop_orco = orco_data->vbo_data[l_index]; + copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]); + loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data; + float *loop_orco = orco_data->vbo_data[ml_index]; + copy_v3_v3(loop_orco, orco_data->orco[ml->v]); + loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ + } +} + +static void extract_orco_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_orco = {.init = extract_orco_init, + .iter_poly_bm = extract_orco_iter_poly_bm, + .iter_poly_mesh = extract_orco_iter_poly_mesh, + .finish = extract_orco_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edge Factor + * Defines how much an edge is visible. + * \{ */ + +typedef struct MeshExtract_EdgeFac_Data { + uchar *vbo_data; + bool use_edge_render; + /* Number of loop per edge. */ + uchar edge_loop_count[0]; +} MeshExtract_EdgeFac_Data; + +static float loop_edge_factor_get(const float f_no[3], + const float v_co[3], + const float v_no[3], + const float v_next_co[3]) +{ + float enor[3], evec[3]; + sub_v3_v3v3(evec, v_next_co, v_co); + cross_v3_v3v3(enor, v_no, evec); + normalize_v3(enor); + float d = fabsf(dot_v3v3(enor, f_no)); + /* Re-scale to the slider range. */ + d *= (1.0f / 0.065f); + CLAMP(d, 0.0f, 1.0f); + return d; +} + +static void *extract_edge_fac_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + MeshExtract_EdgeFac_Data *data; + + if (mr->extract_type == MR_EXTRACT_MESH) { + size_t edge_loop_count_size = sizeof(uint32_t) * mr->edge_len; + data = MEM_callocN(sizeof(*data) + edge_loop_count_size, __func__); + + /* HACK(fclem) Detecting the need for edge render. + * We could have a flag in the mesh instead or check the modifier stack. */ + const MEdge *med = mr->medge; + for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) { + if ((med->flag & ME_EDGERENDER) == 0) { + data->use_edge_render = true; + break; + } + } + } + else { + data = MEM_callocN(sizeof(*data), __func__); + /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */ + data->use_edge_render = true; + } + + data->vbo_data = GPU_vertbuf_get_data(vbo); + return data; +} + +static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_EdgeFac_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + if (BM_edge_is_manifold(l_iter->e)) { + float ratio = loop_edge_factor_get(bm_face_no_get(mr, f), + bm_vert_co_get(mr, l_iter->v), + bm_vert_no_get(mr, l_iter->v), + bm_vert_co_get(mr, l_iter->next->v)); + data->vbo_data[l_index] = ratio * 253 + 1; + } + else { + data->vbo_data[l_index] = 255; + } + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) +{ + MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data; + + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + if (data->use_edge_render) { + const MEdge *med = &mr->medge[ml->e]; + data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0; + } + else { + + /* Count loop per edge to detect non-manifold. */ + if (data->edge_loop_count[ml->e] < 3) { + data->edge_loop_count[ml->e]++; + } + if (data->edge_loop_count[ml->e] == 2) { + /* Manifold */ + const int ml_index_last = mp->totloop + mp->loopstart - 1; + const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); + const MLoop *ml_next = &mr->mloop[ml_index_other]; + const MVert *v1 = &mr->mvert[ml->v]; + const MVert *v2 = &mr->mvert[ml_next->v]; + float vnor_f[3]; + normal_short_to_float_v3(vnor_f, v1->no); + float ratio = loop_edge_factor_get(mr->poly_normals[mp_index], v1->co, vnor_f, v2->co); + data->vbo_data[ml_index] = ratio * 253 + 1; + } + else { + /* Non-manifold */ + data->vbo_data[ml_index] = 255; + } + } + } +} + +static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *UNUSED(eed), + const int ledge_index, + void *_data) +{ + MeshExtract_EdgeFac_Data *data = _data; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255; + data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255; +} + +static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *UNUSED(med), + const uint ledge_index, + void *_data) +{ + MeshExtract_EdgeFac_Data *data = _data; + + data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255; + data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255; +} + +static void extract_edge_fac_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) +{ + GPUVertBuf *vbo = buf; + MeshExtract_EdgeFac_Data *data = _data; + + if (GPU_crappy_amd_driver()) { + /* Some AMD drivers strangely crash with VBO's with a one byte format. + * To workaround we reinitialize the VBO with another format and convert + * all bytes to floats. */ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + /* We keep the data reference in data->vbo_data. */ + data->vbo_data = GPU_vertbuf_steal_data(vbo); + GPU_vertbuf_clear(vbo); + + int buf_len = mr->loop_len + mr->loop_loose_len; + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, buf_len); + + float *fdata = (float *)GPU_vertbuf_get_data(vbo); + for (int ml_index = 0; ml_index < buf_len; ml_index++, fdata++) { + *fdata = data->vbo_data[ml_index] / 255.0f; + } + /* Free old byte data. */ + MEM_freeN(data->vbo_data); + } + MEM_freeN(data); +} + +const MeshExtract extract_edge_fac = { + .init = extract_edge_fac_init, + .iter_poly_bm = extract_edge_fac_iter_poly_bm, + .iter_poly_mesh = extract_edge_fac_iter_poly_mesh, + .iter_ledge_bm = extract_edge_fac_iter_ledge_bm, + .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh, + .finish = extract_edge_fac_finish, + .data_flag = MR_DATA_POLY_NOR, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; + +/** \} */ +/* ---------------------------------------------------------------------- */ +/** \name Extract Vertex Weight + * \{ */ + +typedef struct MeshExtract_Weight_Data { + float *vbo_data; + const DRW_MeshWeightState *wstate; + const MDeformVert *dvert; /* For #Mesh. */ + int cd_ofs; /* For #BMesh. */ +} MeshExtract_Weight_Data; + +static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate) +{ + /* Error state. */ + if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) { + return -2.0f; + } + if (dvert == NULL) { + return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f; + } + + float input = 0.0f; + if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) { + /* Multi-Paint feature */ + bool is_normalized = (wstate->flags & (DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE | + DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE)); + input = BKE_defvert_multipaint_collective_weight(dvert, + wstate->defgroup_len, + wstate->defgroup_sel, + wstate->defgroup_sel_count, + is_normalized); + /* make it black if the selected groups have no weight on a vertex */ + if (input == 0.0f) { + return -1.0f; + } + } + else { + /* default, non tricky behavior */ + input = BKE_defvert_find_weight(dvert, wstate->defgroup_active); + + if (input == 0.0f) { + switch (wstate->alert_mode) { + case OB_DRAW_GROUPUSER_ACTIVE: + return -1.0f; + break; + case OB_DRAW_GROUPUSER_ALL: + if (BKE_defvert_is_weight_zero(dvert, wstate->defgroup_len)) { + return -1.0f; + } + break; + } + } + } + + /* Lock-Relative: display the fraction of current weight vs total unlocked weight. */ + if (wstate->flags & DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE) { + input = BKE_defvert_lock_relative_weight( + input, dvert, wstate->defgroup_len, wstate->defgroup_locked, wstate->defgroup_unlocked); + } + + CLAMP(input, 0.0f, 1.0f); + return input; +} + +static void *extract_weights_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + + MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (float *)GPU_vertbuf_get_data(vbo); + data->wstate = &cache->weight_state; + + if (data->wstate->defgroup_active == -1) { + /* Nothing to show. */ + data->dvert = NULL; + data->cd_ofs = -1; + } + else if (mr->extract_type == MR_EXTRACT_BMESH) { + data->dvert = NULL; + data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT); + } + else { + data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT); + data->cd_ofs = -1; + } + return data; +} + +static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_Weight_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + if (data->cd_ofs != -1) { + const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs); + data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate); + } + else { + data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate); + } + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_Weight_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (data->dvert != NULL) { + const MDeformVert *dvert = &data->dvert[ml->v]; + data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); + } + else { + const MDeformVert *dvert = NULL; + data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate); + } + } +} + +static void extract_weights_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_weights = {.init = extract_weights_init, + .iter_poly_bm = extract_weights_iter_poly_bm, + .iter_poly_mesh = extract_weights_iter_poly_mesh, + .finish = extract_weights_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit Mode Data / Flags + * \{ */ + +typedef struct EditLoopData { + uchar v_flag; + uchar e_flag; + uchar crease; + uchar bweight; +} EditLoopData; + +static void mesh_render_data_face_flag(const MeshRenderData *mr, + BMFace *efa, + const int cd_ofs, + EditLoopData *eattr) +{ + if (efa == mr->efa_act) { + eattr->v_flag |= VFLAG_FACE_ACTIVE; + } + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + eattr->v_flag |= VFLAG_FACE_SELECTED; + } + + if (efa == mr->efa_act_uv) { + eattr->v_flag |= VFLAG_FACE_UV_ACTIVE; + } + if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) { + eattr->v_flag |= VFLAG_FACE_UV_SELECT; + } + +#ifdef WITH_FREESTYLE + if (mr->freestyle_face_ofs != -1) { + const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs); + if (ffa->flag & FREESTYLE_FACE_MARK) { + eattr->v_flag |= VFLAG_FACE_FREESTYLE; + } + } +#endif +} + +static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, EditLoopData *eattr) +{ + const ToolSettings *ts = mr->toolsettings; + const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0; + const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE); + + if (eed == mr->eed_act) { + eattr->e_flag |= VFLAG_EDGE_ACTIVE; + } + if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + eattr->e_flag |= VFLAG_EDGE_SELECTED; + } + if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) { + eattr->e_flag |= VFLAG_EDGE_SELECTED; + eattr->e_flag |= VFLAG_VERT_SELECTED; + } + if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) { + eattr->e_flag |= VFLAG_EDGE_SEAM; + } + if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) { + eattr->e_flag |= VFLAG_EDGE_SHARP; + } + + /* Use active edge color for active face edges because + * specular highlights make it hard to see T55456#510873. + * + * This isn't ideal since it can't be used when mixing edge/face modes + * but it's still better than not being able to see the active face. */ + if (is_face_only_select_mode) { + if (mr->efa_act != NULL) { + if (BM_edge_in_face(eed, mr->efa_act)) { + eattr->e_flag |= VFLAG_EDGE_ACTIVE; + } + } + } + + /* Use a byte for value range */ + if (mr->crease_ofs != -1) { + float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs); + if (crease > 0) { + eattr->crease = (uchar)(crease * 255.0f); + } + } + /* Use a byte for value range */ + if (mr->bweight_ofs != -1) { + float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs); + if (bweight > 0) { + eattr->bweight = (uchar)(bweight * 255.0f); + } + } +#ifdef WITH_FREESTYLE + if (mr->freestyle_edge_ofs != -1) { + const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs); + if (fed->flag & FREESTYLE_EDGE_MARK) { + eattr->e_flag |= VFLAG_EDGE_FREESTYLE; + } + } +#endif +} + +static void mesh_render_data_loop_flag(const MeshRenderData *mr, + BMLoop *l, + const int cd_ofs, + EditLoopData *eattr) +{ + if (cd_ofs == -1) { + return; + } + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_ofs); + if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) { + eattr->v_flag |= VFLAG_VERT_UV_PINNED; + } + if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) { + eattr->v_flag |= VFLAG_VERT_UV_SELECT; + } +} + +static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr, + BMLoop *l, + const int cd_ofs, + EditLoopData *eattr) +{ + if (cd_ofs == -1) { + return; + } + if (uvedit_edge_select_test_ex(mr->toolsettings, l, cd_ofs)) { + eattr->v_flag |= VFLAG_EDGE_UV_SELECT; + eattr->v_flag |= VFLAG_VERT_UV_SELECT; + } +} + +static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, EditLoopData *eattr) +{ + if (eve == mr->eve_act) { + eattr->e_flag |= VFLAG_VERT_ACTIVE; + } + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + eattr->e_flag |= VFLAG_VERT_SELECTED; + } +} + +static void *extract_edit_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING: Adjust #EditLoopData struct accordingly. */ + GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); + GPU_vertformat_alias_add(&format, "flag"); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + return GPU_vertbuf_get_data(vbo); +} + +static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + EditLoopData *data = (EditLoopData *)_data + l_index; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_face_flag(mr, f, -1, data); + mesh_render_data_edge_flag(mr, l_iter->e, data); + mesh_render_data_vert_flag(mr, l_iter->v, data); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + EditLoopData *data = (EditLoopData *)_data + ml_index; + memset(data, 0x0, sizeof(*data)); + BMFace *efa = bm_original_face_get(mr, mp_index); + BMEdge *eed = bm_original_edge_get(mr, ml->e); + BMVert *eve = bm_original_vert_get(mr, ml->v); + if (efa) { + mesh_render_data_face_flag(mr, efa, -1, data); + } + if (eed) { + mesh_render_data_edge_flag(mr, eed, data); + } + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); + } + } +} + +static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2); + memset(data, 0x0, sizeof(*data) * 2); + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + mesh_render_data_vert_flag(mr, eed->v1, &data[0]); + mesh_render_data_vert_flag(mr, eed->v2, &data[1]); +} + +static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *_data) +{ + EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2; + memset(data, 0x0, sizeof(*data) * 2); + const int e_index = mr->ledges[ledge_index]; + BMEdge *eed = bm_original_edge_get(mr, e_index); + BMVert *eve1 = bm_original_vert_get(mr, med->v1); + BMVert *eve2 = bm_original_vert_get(mr, med->v2); + if (eed) { + mesh_render_data_edge_flag(mr, eed, &data[0]); + data[1] = data[0]; + } + if (eve1) { + mesh_render_data_vert_flag(mr, eve1, &data[0]); + } + if (eve2) { + mesh_render_data_vert_flag(mr, eve2, &data[1]); + } +} + +static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *_data) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + mesh_render_data_vert_flag(mr, eve, data); +} + +static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *UNUSED(mv), + const int lvert_index, + void *_data) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + EditLoopData *data = (EditLoopData *)_data + offset + lvert_index; + memset(data, 0x0, sizeof(*data)); + const int v_index = mr->lverts[lvert_index]; + BMVert *eve = bm_original_vert_get(mr, v_index); + if (eve) { + mesh_render_data_vert_flag(mr, eve, data); + } +} + +const MeshExtract extract_edit_data = { + .init = extract_edit_data_init, + .iter_poly_bm = extract_edit_data_iter_poly_bm, + .iter_poly_mesh = extract_edit_data_iter_poly_mesh, + .iter_ledge_bm = extract_edit_data_iter_ledge_bm, + .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh, + .iter_lvert_bm = extract_edit_data_iter_lvert_bm, + .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV Data / Flags + * \{ */ + +typedef struct MeshExtract_EditUVData_Data { + EditLoopData *vbo_data; + int cd_ofs; +} MeshExtract_EditUVData_Data; + +static void *extract_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* WARNING: Adjust #EditLoopData struct accordingly. */ + GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT); + GPU_vertformat_alias_add(&format, "flag"); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + + MeshExtract_EditUVData_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); + data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); + return data; +} + +static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + MeshExtract_EditUVData_Data *data = _data; + EditLoopData *eldata = &data->vbo_data[l_index]; + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) +{ + MeshExtract_EditUVData_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + + EditLoopData *eldata = &data->vbo_data[ml_index]; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, mp_index); + if (efa) { + BMEdge *eed = bm_original_edge_get(mr, ml->e); + BMVert *eve = bm_original_vert_get(mr, ml->v); + if (eed && eve) { + /* Loop on an edge endpoint. */ + BMLoop *l = BM_face_edge_share_loop(efa, eed); + mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata); + mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); + } + else { + if (eed == NULL) { + /* Find if the loop's vert is not part of an edit edge. + * For this, we check if the previous loop was on an edge. */ + const int ml_index_last = mp->loopstart + mp->totloop - 1; + const int l_prev = (ml_index == mp->loopstart) ? ml_index_last : (ml_index - 1); + const MLoop *ml_prev = &mr->mloop[l_prev]; + eed = bm_original_edge_get(mr, ml_prev->e); + } + if (eed) { + /* Mapped points on an edge between two edit verts. */ + BMLoop *l = BM_face_edge_share_loop(efa, eed); + mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata); + } + } + } + } +} + +static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_edituv_data = { + .init = extract_edituv_data_init, + .iter_poly_bm = extract_edituv_data_iter_poly_bm, + .iter_poly_mesh = extract_edituv_data_iter_poly_mesh, + .finish = extract_edituv_data_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV area stretch + * \{ */ + +static void *extract_edituv_stretch_area_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return NULL; +} + +BLI_INLINE float area_ratio_get(float area, float uvarea) +{ + if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) { + /* Tag inversion by using the sign. */ + return (area > uvarea) ? (uvarea / area) : -(area / uvarea); + } + return 0.0f; +} + +BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio) +{ + ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio; + return (ratio > 1.0f) ? (1.0f / ratio) : ratio; +} + +static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(data)) +{ + GPUVertBuf *vbo = buf; + float tot_area = 0.0f, tot_uv_area = 0.0f; + float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + CustomData *cd_ldata = &mr->bm->ldata; + int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV); + + BMFace *efa; + BMIter f_iter; + int f; + BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { + float area = BM_face_calc_area(efa); + float uvarea = BM_face_calc_area_uv(efa, uv_ofs); + tot_area += area; + tot_uv_area += uvarea; + area_ratio[f] = area_ratio_get(area, uvarea); + } + } + else { + BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); + const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + const MPoly *mp = mr->mpoly; + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + float area = BKE_mesh_calc_poly_area(mp, &mr->mloop[mp->loopstart], mr->mvert); + float uvarea = BKE_mesh_calc_poly_uv_area(mp, uv_data); + tot_area += area; + tot_uv_area += uvarea; + area_ratio[mp_index] = area_ratio_get(area, uvarea); + } + } + + cache->tot_area = tot_area; + cache->tot_uv_area = tot_uv_area; + + /* Convert in place to avoid an extra allocation */ + uint16_t *poly_stretch = (uint16_t *)area_ratio; + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { + poly_stretch[mp_index] = area_ratio[mp_index] * SHRT_MAX; + } + + /* Copy face data for each loop. */ + uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMFace *efa; + BMIter f_iter; + int f, l_index = 0; + BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) { + for (int i = 0; i < efa->len; i++, l_index++) { + loop_stretch[l_index] = poly_stretch[f]; + } + } + } + else { + BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); + const MPoly *mp = mr->mpoly; + for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + for (int i = 0; i < mp->totloop; i++, l_index++) { + loop_stretch[l_index] = poly_stretch[mp_index]; + } + } + } + + MEM_freeN(area_ratio); +} + +const MeshExtract extract_edituv_stretch_area = { + .init = extract_edituv_stretch_area_init, + .finish = extract_edituv_stretch_area_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit UV angle stretch + * \{ */ + +typedef struct UVStretchAngle { + int16_t angle; + int16_t uv_angles[2]; +} UVStretchAngle; + +typedef struct MeshExtract_StretchAngle_Data { + UVStretchAngle *vbo_data; + MLoopUV *luv; + float auv[2][2], last_auv[2]; + float av[2][3], last_av[3]; + int cd_ofs; +} MeshExtract_StretchAngle_Data; + +static void compute_normalize_edge_vectors(float auv[2][2], + float av[2][3], + const float uv[2], + const float uv_prev[2], + const float co[3], + const float co_prev[3]) +{ + /* Move previous edge. */ + copy_v2_v2(auv[0], auv[1]); + copy_v3_v3(av[0], av[1]); + /* 2d edge */ + sub_v2_v2v2(auv[1], uv_prev, uv); + normalize_v2(auv[1]); + /* 3d edge */ + sub_v3_v3v3(av[1], co_prev, co); + normalize_v3(av[1]); +} + +static short v2_to_short_angle(const float v[2]) +{ + return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX; +} + +static void edituv_get_edituv_stretch_angle(float auv[2][2], + const float av[2][3], + UVStretchAngle *r_stretch) +{ + /* Send UV's to the shader and let it compute the aspect corrected angle. */ + r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]); + r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]); + /* Compute 3D angle here. */ + r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX; + +#if 0 /* here for reference, this is done in shader now. */ + float uvang = angle_normalized_v2v2(auv0, auv1); + float ang = angle_normalized_v3v3(av0, av1); + float stretch = fabsf(uvang - ang) / (float)M_PI; + return 1.0f - pow2f(1.0f - stretch); +#endif +} + +static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* Waning: adjust #UVStretchAngle struct accordingly. */ + GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); + GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo); + + /* Special iterator needed to save about half of the computing cost. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); + } + else { + BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); + data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + } + return data; +} + +static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_StretchAngle_Data *data = _data; + float(*auv)[2] = data->auv, *last_auv = data->last_auv; + float(*av)[3] = data->av, *last_av = data->last_av; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + + const MLoopUV *luv, *luv_next; + BMLoop *l_next = l_iter->next; + if (l_iter == BM_FACE_FIRST_LOOP(f)) { + /* First loop in face. */ + BMLoop *l_tmp = l_iter->prev; + BMLoop *l_next_tmp = l_iter; + luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs); + luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs); + compute_normalize_edge_vectors(auv, + av, + luv->uv, + luv_next->uv, + bm_vert_co_get(mr, l_tmp->v), + bm_vert_co_get(mr, l_next_tmp->v)); + /* Save last edge. */ + copy_v2_v2(last_auv, auv[1]); + copy_v3_v3(last_av, av[1]); + } + if (l_next == BM_FACE_FIRST_LOOP(f)) { + /* Move previous edge. */ + copy_v2_v2(auv[0], auv[1]); + copy_v3_v3(av[0], av[1]); + /* Copy already calculated last edge. */ + copy_v2_v2(auv[1], last_auv); + copy_v3_v3(av[1], last_av); + } + else { + luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); + luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs); + compute_normalize_edge_vectors(auv, + av, + luv->uv, + luv_next->uv, + bm_vert_co_get(mr, l_iter->v), + bm_vert_co_get(mr, l_next->v)); + } + edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *_data) +{ + MeshExtract_StretchAngle_Data *data = _data; + + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + float(*auv)[2] = data->auv, *last_auv = data->last_auv; + float(*av)[3] = data->av, *last_av = data->last_av; + int l_next = ml_index + 1; + const MVert *v, *v_next; + if (ml_index == mp->loopstart) { + /* First loop in face. */ + const int ml_index_last = ml_index_end - 1; + const int l_next_tmp = mp->loopstart; + v = &mr->mvert[mr->mloop[ml_index_last].v]; + v_next = &mr->mvert[mr->mloop[l_next_tmp].v]; + compute_normalize_edge_vectors( + auv, av, data->luv[ml_index_last].uv, data->luv[l_next_tmp].uv, v->co, v_next->co); + /* Save last edge. */ + copy_v2_v2(last_auv, auv[1]); + copy_v3_v3(last_av, av[1]); + } + if (l_next == ml_index_end) { + l_next = mp->loopstart; + /* Move previous edge. */ + copy_v2_v2(auv[0], auv[1]); + copy_v3_v3(av[0], av[1]); + /* Copy already calculated last edge. */ + copy_v2_v2(auv[1], last_auv); + copy_v3_v3(av[1], last_av); + } + else { + v = &mr->mvert[mr->mloop[ml_index].v]; + v_next = &mr->mvert[mr->mloop[l_next].v]; + compute_normalize_edge_vectors( + auv, av, data->luv[ml_index].uv, data->luv[l_next].uv, v->co, v_next->co); + } + edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]); + } +} + +static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_edituv_stretch_angle = { + .init = extract_edituv_stretch_angle_init, + .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm, + .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh, + .finish = extract_edituv_stretch_angle_finish, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Edit Mesh Analysis Colors + * \{ */ + +static void *extract_mesh_analysis_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len); + + return NULL; +} + +static void axis_from_enum_v3(float v[3], const char axis) +{ + zero_v3(v); + if (axis < 3) { + v[axis] = 1.0f; + } + else { + v[axis - 3] = -1.0f; + } +} + +BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange) +{ + if (fac < min) { + fac = 1.0f; + } + else if (fac > max) { + fac = -1.0f; + } + else { + fac = (fac - min) * minmax_irange; + fac = 1.0f - fac; + CLAMP(fac, 0.0f, 1.0f); + } + return fac; +} + +static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang) +{ + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->overhang_min / (float)M_PI; + const float max = statvis->overhang_max / (float)M_PI; + const char axis = statvis->overhang_axis; + BMEditMesh *em = mr->edit_bmesh; + BMIter iter; + BMesh *bm = em->bm; + BMFace *f; + float dir[3]; + const float minmax_irange = 1.0f / (max - min); + + BLI_assert(min <= max); + + axis_from_enum_v3(dir, axis); + + /* now convert into global space */ + mul_transposed_mat3_m4_v3(mr->obmat, dir); + normalize_v3(dir); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + int l_index = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / (float)M_PI; + fac = overhang_remap(fac, min, max, minmax_irange); + for (int i = 0; i < f->len; i++, l_index++) { + r_overhang[l_index] = fac; + } + } + } + else { + const MPoly *mp = mr->mpoly; + for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + float fac = angle_normalized_v3v3(mr->poly_normals[mp_index], dir) / (float)M_PI; + fac = overhang_remap(fac, min, max, minmax_irange); + for (int i = 0; i < mp->totloop; i++, l_index++) { + r_overhang[l_index] = fac; + } + } + } +} + +/** + * Needed so we can use jitter values for face interpolation. + */ +static void uv_from_jitter_v2(float uv[2]) +{ + uv[0] += 0.5f; + uv[1] += 0.5f; + if (uv[0] + uv[1] > 1.0f) { + uv[0] = 1.0f - uv[0]; + uv[1] = 1.0f - uv[1]; + } + + clamp_v2(uv, 0.0f, 1.0f); +} + +BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange) +{ + /* important not '<=' */ + if (fac < max) { + fac = (fac - min) * minmax_irange; + fac = 1.0f - fac; + CLAMP(fac, 0.0f, 1.0f); + } + else { + fac = -1.0f; + } + return fac; +} + +static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness) +{ + const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */ + /* cheating to avoid another allocation */ + float *face_dists = r_thickness + (mr->loop_len - mr->poly_len); + BMEditMesh *em = mr->edit_bmesh; + const float scale = 1.0f / mat4_to_scale(mr->obmat); + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->thickness_min * scale; + const float max = statvis->thickness_max * scale; + const float minmax_irange = 1.0f / (max - min); + const int samples = statvis->thickness_samples; + float jit_ofs[32][2]; + BLI_assert(samples <= 32); + BLI_assert(min <= max); + + copy_vn_fl(face_dists, mr->poly_len, max); + + BLI_jitter_init(jit_ofs, samples); + for (int j = 0; j < samples; j++) { + uv_from_jitter_v2(jit_ofs[j]); + } + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMesh *bm = em->bm; + BM_mesh_elem_index_ensure(bm, BM_FACE); + + struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); + struct BMLoop *(*looptris)[3] = em->looptris; + for (int i = 0; i < mr->tri_len; i++) { + BMLoop **ltri = looptris[i]; + const int index = BM_elem_index_get(ltri[0]->f); + const float *cos[3] = { + bm_vert_co_get(mr, ltri[0]->v), + bm_vert_co_get(mr, ltri[1]->v), + bm_vert_co_get(mr, ltri[2]->v), + }; + float ray_co[3]; + float ray_no[3]; + + normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); + + for (int j = 0; j < samples; j++) { + float dist = face_dists[index]; + interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); + madd_v3_v3fl(ray_co, ray_no, eps_offset); + + BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL); + if (f_hit && dist < face_dists[index]) { + float angle_fac = fabsf( + dot_v3v3(bm_face_no_get(mr, ltri[0]->f), bm_face_no_get(mr, f_hit))); + angle_fac = 1.0f - angle_fac; + angle_fac = angle_fac * angle_fac * angle_fac; + angle_fac = 1.0f - angle_fac; + dist /= angle_fac; + if (dist < face_dists[index]) { + face_dists[index] = dist; + } + } + } + } + BKE_bmbvh_free(bmtree); + + BMIter iter; + BMFace *f; + int l_index = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + float fac = face_dists[BM_elem_index_get(f)]; + fac = thickness_remap(fac, min, max, minmax_irange); + for (int i = 0; i < f->len; i++, l_index++) { + r_thickness[l_index] = fac; + } + } + } + else { + BVHTreeFromMesh treeData = {NULL}; + + BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); + const MLoopTri *mlooptri = mr->mlooptri; + for (int i = 0; i < mr->tri_len; i++, mlooptri++) { + const int index = mlooptri->poly; + const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co, + mr->mvert[mr->mloop[mlooptri->tri[1]].v].co, + mr->mvert[mr->mloop[mlooptri->tri[2]].v].co}; + float ray_co[3]; + float ray_no[3]; + + normal_tri_v3(ray_no, cos[2], cos[1], cos[0]); + + for (int j = 0; j < samples; j++) { + interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]); + madd_v3_v3fl(ray_co, ray_no, eps_offset); + + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = face_dists[index]; + if ((BLI_bvhtree_ray_cast( + tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) && + hit.dist < face_dists[index]) { + float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no)); + angle_fac = 1.0f - angle_fac; + angle_fac = angle_fac * angle_fac * angle_fac; + angle_fac = 1.0f - angle_fac; + hit.dist /= angle_fac; + if (hit.dist < face_dists[index]) { + face_dists[index] = hit.dist; + } + } + } + } + + const MPoly *mp = mr->mpoly; + for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + float fac = face_dists[mp_index]; + fac = thickness_remap(fac, min, max, minmax_irange); + for (int i = 0; i < mp->totloop; i++, l_index++) { + r_thickness[l_index] = fac; + } + } + } +} + +struct BVHTree_OverlapData { + const Mesh *me; + const MLoopTri *mlooptri; + float epsilon; +}; + +static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) +{ + struct BVHTree_OverlapData *data = userdata; + const Mesh *me = data->me; + + const MLoopTri *tri_a = &data->mlooptri[index_a]; + const MLoopTri *tri_b = &data->mlooptri[index_b]; + + if (UNLIKELY(tri_a->poly == tri_b->poly)) { + return false; + } + + const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co, + me->mvert[me->mloop[tri_a->tri[1]].v].co, + me->mvert[me->mloop[tri_a->tri[2]].v].co}; + const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co, + me->mvert[me->mloop[tri_b->tri[1]].v].co, + me->mvert[me->mloop[tri_b->tri[2]].v].co}; + float ix_pair[2][3]; + int verts_shared = 0; + + verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) + + ELEM(tri_a_co[2], UNPACK3(tri_b_co))); + + /* if 2 points are shared, bail out */ + if (verts_shared >= 2) { + return false; + } + + return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) && + /* if we share a vertex, check the intersection isn't a 'point' */ + ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon))); +} + +static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect) +{ + BMEditMesh *em = mr->edit_bmesh; + + for (int l_index = 0; l_index < mr->loop_len; l_index++) { + r_intersect[l_index] = -1.0f; + } + + if (mr->extract_type == MR_EXTRACT_BMESH) { + uint overlap_len; + BMesh *bm = em->bm; + + BM_mesh_elem_index_ensure(bm, BM_FACE); + + struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); + BVHTreeOverlap *overlap = BKE_bmbvh_overlap_self(bmtree, &overlap_len); + + if (overlap) { + for (int i = 0; i < overlap_len; i++) { + BMFace *f_hit_pair[2] = { + em->looptris[overlap[i].indexA][0]->f, + em->looptris[overlap[i].indexB][0]->f, + }; + for (int j = 0; j < 2; j++) { + BMFace *f_hit = f_hit_pair[j]; + BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit); + int l_index = BM_elem_index_get(l_first); + for (int k = 0; k < f_hit->len; k++, l_index++) { + r_intersect[l_index] = 1.0f; + } + } + } + MEM_freeN(overlap); + } + + BKE_bmbvh_free(bmtree); + } + else { + uint overlap_len; + BVHTreeFromMesh treeData = {NULL}; + + BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4); + + struct BVHTree_OverlapData data = { + .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)}; + + BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data); + if (overlap) { + for (int i = 0; i < overlap_len; i++) { + const MPoly *f_hit_pair[2] = { + &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly], + &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly], + }; + for (int j = 0; j < 2; j++) { + const MPoly *f_hit = f_hit_pair[j]; + int l_index = f_hit->loopstart; + for (int k = 0; k < f_hit->totloop; k++, l_index++) { + r_intersect[l_index] = 1.0f; + } + } + } + MEM_freeN(overlap); + } + } +} + +BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange) +{ + if (fac >= min) { + fac = (fac - min) * minmax_irange; + CLAMP(fac, 0.0f, 1.0f); + } + else { + /* fallback */ + fac = -1.0f; + } + return fac; +} + +static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort) +{ + BMEditMesh *em = mr->edit_bmesh; + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->distort_min; + const float max = statvis->distort_max; + const float minmax_irange = 1.0f / (max - min); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMesh *bm = em->bm; + BMFace *f; + + if (mr->bm_vert_coords != NULL) { + BKE_editmesh_cache_ensure_poly_normals(em, mr->edit_data); + + /* Most likely this is already valid, ensure just in case. + * Needed for #BM_loop_calc_face_normal_safe_vcos. */ + BM_mesh_elem_index_ensure(em->bm, BM_VERT); + } + + int l_index = 0; + int f_index = 0; + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) { + float fac = -1.0f; + + if (f->len > 3) { + BMLoop *l_iter, *l_first; + + fac = 0.0f; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const float *no_face; + float no_corner[3]; + if (mr->bm_vert_coords != NULL) { + no_face = mr->bm_poly_normals[f_index]; + BM_loop_calc_face_normal_safe_vcos(l_iter, no_face, mr->bm_vert_coords, no_corner); + } + else { + no_face = f->no; + BM_loop_calc_face_normal_safe(l_iter, no_corner); + } + + /* simple way to detect (what is most likely) concave */ + if (dot_v3v3(no_face, no_corner) < 0.0f) { + negate_v3(no_corner); + } + fac = max_ff(fac, angle_normalized_v3v3(no_face, no_corner)); + + } while ((l_iter = l_iter->next) != l_first); + fac *= 2.0f; + } + + fac = distort_remap(fac, min, max, minmax_irange); + for (int i = 0; i < f->len; i++, l_index++) { + r_distort[l_index] = fac; + } + } + } + else { + const MPoly *mp = mr->mpoly; + for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + float fac = -1.0f; + + if (mp->totloop > 3) { + float *f_no = mr->poly_normals[mp_index]; + fac = 0.0f; + + for (int i = 1; i <= mp->totloop; i++) { + const MLoop *l_prev = &mr->mloop[mp->loopstart + (i - 1) % mp->totloop]; + const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop]; + const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop]; + float no_corner[3]; + normal_tri_v3(no_corner, + mr->mvert[l_prev->v].co, + mr->mvert[l_curr->v].co, + mr->mvert[l_next->v].co); + /* simple way to detect (what is most likely) concave */ + if (dot_v3v3(f_no, no_corner) < 0.0f) { + negate_v3(no_corner); + } + fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner)); + } + fac *= 2.0f; + } + + fac = distort_remap(fac, min, max, minmax_irange); + for (int i = 0; i < mp->totloop; i++, l_index++) { + r_distort[l_index] = fac; + } + } + } +} + +BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange) +{ + /* important not '>=' */ + if (fac > min) { + fac = (fac - min) * minmax_irange; + CLAMP(fac, 0.0f, 1.0f); + } + else { + /* fallback */ + fac = -1.0f; + } + return fac; +} + +static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) +{ + BMEditMesh *em = mr->edit_bmesh; + const MeshStatVis *statvis = &mr->toolsettings->statvis; + const float min = statvis->sharp_min; + const float max = statvis->sharp_max; + const float minmax_irange = 1.0f / (max - min); + + /* Can we avoid this extra allocation? */ + float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__); + copy_vn_fl(vert_angles, mr->vert_len, -M_PI); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMIter iter; + BMesh *bm = em->bm; + BMFace *efa; + BMEdge *e; + /* first assign float values to verts */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + float angle = BM_edge_calc_face_angle_signed(e); + float *col1 = &vert_angles[BM_elem_index_get(e->v1)]; + float *col2 = &vert_angles[BM_elem_index_get(e->v2)]; + *col1 = max_ff(*col1, angle); + *col2 = max_ff(*col2, angle); + } + /* Copy vert value to loops. */ + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + int l_index = BM_elem_index_get(l_iter); + int v_index = BM_elem_index_get(l_iter->v); + r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange); + } while ((l_iter = l_iter->next) != l_first); + } + } + else { + /* first assign float values to verts */ + const MPoly *mp = mr->mpoly; + + EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len); + + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) { + for (int i = 0; i < mp->totloop; i++) { + const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop]; + const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop]; + const MVert *v_curr = &mr->mvert[l_curr->v]; + const MVert *v_next = &mr->mvert[l_next->v]; + float angle; + void **pval; + bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval); + if (!value_is_init) { + *pval = mr->poly_normals[mp_index]; + /* non-manifold edge, yet... */ + continue; + } + if (*pval != NULL) { + const float *f1_no = mr->poly_normals[mp_index]; + const float *f2_no = *pval; + angle = angle_normalized_v3v3(f1_no, f2_no); + angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle; + /* Tag as manifold. */ + *pval = NULL; + } + else { + /* non-manifold edge */ + angle = DEG2RADF(90.0f); + } + float *col1 = &vert_angles[l_curr->v]; + float *col2 = &vert_angles[l_next->v]; + *col1 = max_ff(*col1, angle); + *col2 = max_ff(*col2, angle); + } + } + /* Remaining non manifold edges. */ + EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh); + for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) { + if (BLI_edgehashIterator_getValue(ehi) != NULL) { + uint v1, v2; + const float angle = DEG2RADF(90.0f); + BLI_edgehashIterator_getKey(ehi, &v1, &v2); + float *col1 = &vert_angles[v1]; + float *col2 = &vert_angles[v2]; + *col1 = max_ff(*col1, angle); + *col2 = max_ff(*col2, angle); + } + } + BLI_edgehashIterator_free(ehi); + BLI_edgehash_free(eh, NULL); + + const MLoop *ml = mr->mloop; + for (int l_index = 0; l_index < mr->loop_len; l_index++, ml++) { + r_sharp[l_index] = sharp_remap(vert_angles[ml->v], min, max, minmax_irange); + } + } + + MEM_freeN(vert_angles); +} + +static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) +{ + GPUVertBuf *vbo = buf; + BLI_assert(mr->edit_bmesh); + + float *l_weight = (float *)GPU_vertbuf_get_data(vbo); + + switch (mr->toolsettings->statvis.type) { + case SCE_STATVIS_OVERHANG: + statvis_calc_overhang(mr, l_weight); + break; + case SCE_STATVIS_THICKNESS: + statvis_calc_thickness(mr, l_weight); + break; + case SCE_STATVIS_INTERSECT: + statvis_calc_intersect(mr, l_weight); + break; + case SCE_STATVIS_DISTORT: + statvis_calc_distort(mr, l_weight); + break; + case SCE_STATVIS_SHARP: + statvis_calc_sharp(mr, l_weight); + break; + } +} + +const MeshExtract extract_mesh_analysis = { + .init = extract_mesh_analysis_init, + .finish = extract_analysis_iter_finish_mesh, + /* This is not needed for all visualization types. + * * Maybe split into different extract. */ + .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots positions + * \{ */ + +static void *extract_fdots_pos_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + return GPU_vertbuf_get_data(vbo); +} + +static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int f_index, + void *data) +{ + float(*center)[3] = data; + + float *co = center[f_index]; + zero_v3(co); + + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); + } while ((l_iter = l_iter->next) != l_first); + mul_v3_fl(co, 1.0f / (float)f->len); +} + +static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + float(*center)[3] = (float(*)[3])data; + float *co = center[mp_index]; + zero_v3(co); + + const MVert *mvert = mr->mvert; + const MLoop *mloop = mr->mloop; + + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { + const MVert *mv = &mr->mvert[ml->v]; + if (mv->flag & ME_VERT_FACEDOT) { + copy_v3_v3(center[mp_index], mv->co); + break; + } + } + else { + const MVert *mv = &mvert[ml->v]; + add_v3_v3(center[mp_index], mv->co); + } + } + + if (!mr->use_subsurf_fdots) { + mul_v3_fl(co, 1.0f / (float)mp->totloop); + } +} + +const MeshExtract extract_fdots_pos = { + .init = extract_fdots_pos_init, + .iter_poly_bm = extract_fdots_pos_iter_poly_bm, + .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Normal and edit flag + * \{ */ +#define NOR_AND_FLAG_DEFAULT 0 +#define NOR_AND_FLAG_SELECT 1 +#define NOR_AND_FLAG_ACTIVE -1 +#define NOR_AND_FLAG_HIDDEN -2 + +static void *extract_fdots_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + return NULL; +} + +static void extract_fdots_nor_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) +{ + GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; + GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo); + BMFace *efa; + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + for (int f = 0; f < mr->poly_len; f++) { + efa = BM_face_at_index(mr->bm, f); + const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN); + if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[f] == ORIGINDEX_NONE)) { + nor[f] = GPU_normal_convert_i10_v3(invalid_normal); + nor[f].w = NOR_AND_FLAG_HIDDEN; + } + else { + nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa)); + /* Select / Active Flag. */ + nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? + ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : + NOR_AND_FLAG_DEFAULT); + } + } + } + else { + for (int f = 0; f < mr->poly_len; f++) { + efa = bm_original_face_get(mr, f); + const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN); + if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[f] == ORIGINDEX_NONE)) { + nor[f] = GPU_normal_convert_i10_v3(invalid_normal); + nor[f].w = NOR_AND_FLAG_HIDDEN; + } + else { + nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa)); + /* Select / Active Flag. */ + nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? + ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : + NOR_AND_FLAG_DEFAULT); + } + } + } +} + +const MeshExtract extract_fdots_nor = { + .init = extract_fdots_nor_init, + .finish = extract_fdots_nor_finish, + .data_flag = MR_DATA_POLY_NOR, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots High Quality Normal and edit flag + * \{ */ +static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + return NULL; +} + +static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) +{ + GPUVertBuf *vbo = buf; + static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; + short *nor = (short *)GPU_vertbuf_get_data(vbo); + BMFace *efa; + + /* Quicker than doing it for each loop. */ + if (mr->extract_type == MR_EXTRACT_BMESH) { + for (int f = 0; f < mr->poly_len; f++) { + efa = BM_face_at_index(mr->bm, f); + const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN); + if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[f] == ORIGINDEX_NONE)) { + normal_float_to_short_v3(&nor[f * 4], invalid_normal); + nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN; + } + else { + normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa)); + /* Select / Active Flag. */ + nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? + ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : + NOR_AND_FLAG_DEFAULT); + } + } + } + else { + for (int f = 0; f < mr->poly_len; f++) { + efa = bm_original_face_get(mr, f); + const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN); + if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && + mr->p_origindex[f] == ORIGINDEX_NONE)) { + normal_float_to_short_v3(&nor[f * 4], invalid_normal); + nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN; + } + else { + normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa)); + /* Select / Active Flag. */ + nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ? + ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) : + NOR_AND_FLAG_DEFAULT); + } + } + } +} + +const MeshExtract extract_fdots_nor_hq = { + .init = extract_fdots_nor_hq_init, + .finish = extract_fdots_nor_hq_finish, + .data_flag = MR_DATA_POLY_NOR, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots UV + * \{ */ + +typedef struct MeshExtract_FdotUV_Data { + float (*vbo_data)[2]; + MLoopUV *uv_data; + int cd_ofs; +} MeshExtract_FdotUV_Data; + +static void *extract_fdots_uv_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPU_vertformat_alias_add(&format, "au"); + GPU_vertformat_alias_add(&format, "pos"); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + if (!mr->use_subsurf_fdots) { + /* Clear so we can accumulate on it. */ + memset(GPU_vertbuf_get_data(vbo), 0x0, mr->poly_len * GPU_vertbuf_get_format(vbo)->stride); + } + + MeshExtract_FdotUV_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (float(*)[2])GPU_vertbuf_get_data(vbo); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); + } + else { + data->uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + } + return data; +} + +static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_FdotUV_Data *data = _data; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + float w = 1.0f / (float)f->len; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs); + madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) +{ + MeshExtract_FdotUV_Data *data = _data; + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + if (mr->use_subsurf_fdots) { + const MVert *mv = &mr->mvert[ml->v]; + if (mv->flag & ME_VERT_FACEDOT) { + copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv); + } + } + else { + float w = 1.0f / (float)mp->totloop; + madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w); + } + } +} + +static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_fdots_uv = { + .init = extract_fdots_uv_init, + .iter_poly_bm = extract_fdots_uv_iter_poly_bm, + .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh, + .finish = extract_fdots_uv_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Facedots Edit UV flag + * \{ */ + +typedef struct MeshExtract_EditUVFdotData_Data { + EditLoopData *vbo_data; + int cd_ofs; +} MeshExtract_EditUVFdotData_Data; + +static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + + MeshExtract_EditUVFdotData_Data *data = MEM_callocN(sizeof(*data), __func__); + data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo); + data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); + return data; +} + +static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr, + BMFace *f, + const int UNUSED(f_index), + void *_data) +{ + MeshExtract_EditUVFdotData_Data *data = _data; + EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)]; + memset(eldata, 0x0, sizeof(*eldata)); + mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata); +} + +static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *UNUSED(mp), + const int mp_index, + void *_data) +{ + MeshExtract_EditUVFdotData_Data *data = _data; + EditLoopData *eldata = &data->vbo_data[mp_index]; + memset(eldata, 0x0, sizeof(*eldata)); + BMFace *efa = bm_original_face_get(mr, mp_index); + if (efa) { + mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata); + } +} + +static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) +{ + MEM_freeN(data); +} + +const MeshExtract extract_fdots_edituv_data = { + .init = extract_fdots_edituv_data_init, + .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm, + .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh, + .finish = extract_fdots_edituv_data_finish, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Skin Modifier Roots + * \{ */ + +typedef struct SkinRootData { + float size; + float local_pos[3]; +} SkinRootData; + +static void *extract_skin_roots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + /* Exclusively for edit mode. */ + BLI_assert(mr->bm); + + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->bm->totvert); + + SkinRootData *vbo_data = (SkinRootData *)GPU_vertbuf_get_data(vbo); + + int root_len = 0; + int cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MVERT_SKIN); + + BMIter iter; + BMVert *eve; + BM_ITER_MESH (eve, &iter, mr->bm, BM_VERTS_OF_MESH) { + const MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_ofs); + if (vs->flag & MVERT_SKIN_ROOT) { + vbo_data->size = (vs->radius[0] + vs->radius[1]) * 0.5f; + copy_v3_v3(vbo_data->local_pos, bm_vert_co_get(mr, eve)); + vbo_data++; + root_len++; + } + } + + /* It's really unlikely that all verts will be roots. Resize to avoid losing VRAM. */ + GPU_vertbuf_data_len_set(vbo, root_len); + + return NULL; +} + +const MeshExtract extract_skin_roots = { + .init = extract_skin_roots_init, + .data_flag = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Selection Index + * \{ */ + +static void *extract_select_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* TODO rename "color" to something more descriptive. */ + GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); + } + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len); + return GPU_vertbuf_get_data(vbo); +} + +/* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the + * select element associated with this loop ID. This would remove the need for this separate + * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the + * shader to output original index. */ + +static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int f_index, + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = f_index; + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->e); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *f, + const int UNUSED(f_index), + void *data) +{ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const int l_index = BM_elem_index_get(l_iter); + ((uint32_t *)data)[l_index] = BM_elem_index_get(l_iter->v); + } while ((l_iter = l_iter->next) != l_first); +} + +static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *data) +{ + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed); +} + +static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *data) +{ + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1); + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2); +} + +static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *data) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve); +} + +static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data) +{ + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index; + } +} + +static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e; + } +} + +static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int UNUSED(mp_index), + void *data) +{ + const MLoop *mloop = mr->mloop; + const int ml_index_end = mp->loopstart + mp->totloop; + for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { + const MLoop *ml = &mloop[ml_index]; + ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v; + } +} + +static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *UNUSED(med), + const uint ledge_index, + void *data) +{ + const int e_index = mr->ledges[ledge_index]; + const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig; +} + +static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *data) +{ + int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1; + int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig; + ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig; +} + +static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr, + const MVert *UNUSED(mv), + const int lvert_index, + void *data) +{ + const int offset = mr->loop_len + (mr->edge_loose_len * 2); + + const int v_index = mr->lverts[lvert_index]; + const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index; + ((uint32_t *)data)[offset + lvert_index] = v_orig; +} + +const MeshExtract extract_poly_idx = { + .init = extract_select_idx_init, + .iter_poly_bm = extract_poly_idx_iter_poly_bm, + .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; + +const MeshExtract extract_edge_idx = { + .init = extract_select_idx_init, + .iter_poly_bm = extract_edge_idx_iter_poly_bm, + .iter_poly_mesh = extract_edge_idx_iter_poly_mesh, + .iter_ledge_bm = extract_edge_idx_iter_ledge_bm, + .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; + +const MeshExtract extract_vert_idx = { + .init = extract_select_idx_init, + .iter_poly_bm = extract_vert_idx_iter_poly_bm, + .iter_poly_mesh = extract_vert_idx_iter_poly_mesh, + .iter_ledge_bm = extract_vert_idx_iter_ledge_bm, + .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh, + .iter_lvert_bm = extract_vert_idx_iter_lvert_bm, + .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; + +static void *extract_fdot_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) +{ + GPUVertBuf *vbo = buf; + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* TODO rename "color" to something more descriptive. */ + GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT); + } + + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, mr->poly_len); + return GPU_vertbuf_get_data(vbo); +} + +static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr), + BMFace *UNUSED(f), + const int f_index, + void *data) +{ + ((uint32_t *)data)[f_index] = f_index; +} + +static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *UNUSED(mp), + const int mp_index, + void *data) +{ + if (mr->p_origindex != NULL) { + ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index]; + } + else { + ((uint32_t *)data)[mp_index] = mp_index; + } +} + +const MeshExtract extract_fdot_idx = { + .init = extract_fdot_idx_init, + .iter_poly_bm = extract_fdot_idx_iter_poly_bm, + .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, + .data_flag = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; \ No newline at end of file diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h new file mode 100644 index 00000000000..3360b90139d --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -0,0 +1,509 @@ +/* + * 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 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#pragma once + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_editmesh.h" + +#include "draw_cache_extract.h" + +typedef enum eMRExtractType { + MR_EXTRACT_BMESH, + MR_EXTRACT_MAPPED, + MR_EXTRACT_MESH, +} eMRExtractType; + +typedef struct MeshRenderData { + eMRExtractType extract_type; + + int poly_len, edge_len, vert_len, loop_len; + int edge_loose_len; + int vert_loose_len; + int loop_loose_len; + int tri_len; + int mat_len; + + bool use_hide; + bool use_subsurf_fdots; + bool use_final_mesh; + + /** Use for #MeshStatVis calculation which use world-space coords. */ + float obmat[4][4]; + + const ToolSettings *toolsettings; + /** Edit Mesh */ + BMEditMesh *edit_bmesh; + BMesh *bm; + EditMeshData *edit_data; + + /* For deformed edit-mesh data. */ + /* Use for #ME_WRAPPER_TYPE_BMESH. */ + const float (*bm_vert_coords)[3]; + const float (*bm_vert_normals)[3]; + const float (*bm_poly_normals)[3]; + const float (*bm_poly_centers)[3]; + + int *v_origindex, *e_origindex, *p_origindex; + int crease_ofs; + int bweight_ofs; + int freestyle_edge_ofs; + int freestyle_face_ofs; + /** Mesh */ + Mesh *me; + const MVert *mvert; + const MEdge *medge; + const MLoop *mloop; + const MPoly *mpoly; + BMVert *eve_act; + BMEdge *eed_act; + BMFace *efa_act; + BMFace *efa_act_uv; + /* Data created on-demand (usually not for #BMesh based data). */ + MLoopTri *mlooptri; + float (*loop_normals)[3]; + float (*poly_normals)[3]; + int *lverts, *ledges; +} MeshRenderData; + +BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) +{ + return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_face_at_index(mr->bm, mr->p_origindex[idx]) : + NULL; +} + +BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx) +{ + return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_edge_at_index(mr->bm, mr->e_origindex[idx]) : + NULL; +} + +BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx) +{ + return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ? + BM_vert_at_index(mr->bm, mr->v_origindex[idx]) : + NULL; +} + +BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve) +{ + const float(*vert_coords)[3] = mr->bm_vert_coords; + if (vert_coords != NULL) { + return vert_coords[BM_elem_index_get(eve)]; + } + + UNUSED_VARS(mr); + return eve->co; +} + +BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve) +{ + const float(*vert_normals)[3] = mr->bm_vert_normals; + if (vert_normals != NULL) { + return vert_normals[BM_elem_index_get(eve)]; + } + + UNUSED_VARS(mr); + return eve->no; +} + +BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa) +{ + const float(*poly_normals)[3] = mr->bm_poly_normals; + if (poly_normals != NULL) { + return poly_normals[BM_elem_index_get(efa)]; + } + + UNUSED_VARS(mr); + return efa->no; +} + +/* TODO(jbakker): phase out batch iteration macros as they are only used once. */ +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loop Triangles + * \{ */ + +typedef struct ExtractTriBMesh_Params { + BMLoop *(*looptris)[3]; + int tri_range[2]; +} ExtractTriBMesh_Params; +typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr, + BMLoop **elt, + const int elt_index, + void *data); + +#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \ + CHECK_TYPE(params, const ExtractTriBMesh_Params *); \ + { \ + const int _tri_index_end = (params)->tri_range[1]; \ + BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \ + for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ + index_tri += 1, elem_tri += 3) +#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END } + +typedef struct ExtractTriMesh_Params { + const MLoopTri *mlooptri; + int tri_range[2]; +} ExtractTriMesh_Params; +typedef void(ExtractTriMeshFn)(const MeshRenderData *mr, + const MLoopTri *mlt, + const int elt_index, + void *data); + +#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \ + CHECK_TYPE(params, const ExtractTriMesh_Params *); \ + { \ + const int _tri_index_end = (params)->tri_range[1]; \ + const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \ + for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \ + index_tri += 1, elem_tri += 1) +#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Polygons, Loops + * \{ */ + +typedef struct ExtractPolyBMesh_Params { + BMLoop *(*looptris)[3]; + int poly_range[2]; +} ExtractPolyBMesh_Params; +typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr, + BMFace *f, + const int f_index, + void *data); + +#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \ + CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMFace **_ftable = mr->bm->ftable; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + BMFace *elem_poly = _ftable[index_poly]; \ + (void)elem_poly; + +#define EXTRACT_POLY_FOREACH_BM_END \ + } \ + } + +/* Iterate over polygon and loop. */ +#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \ + CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMFace **_ftable = mr->bm->ftable; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + BMFace *elem_face = _ftable[index_poly]; \ + BMLoop *elem_loop, *l_first; \ + elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \ + do { \ + const int index_loop = BM_elem_index_get(elem_loop); \ + (void)index_loop; /* Quiet warning when unused. */ + +#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \ + } \ + while ((elem_loop = elem_loop->next) != l_first) \ + ; \ + } \ + } + +typedef struct ExtractPolyMesh_Params { + int poly_range[2]; +} ExtractPolyMesh_Params; +typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *data); + +#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \ + CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ + { \ + const MPoly *_mpoly = mr->mpoly; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + const MPoly *elem_poly = &_mpoly[index_poly]; \ + (void)elem_poly; + +#define EXTRACT_POLY_FOREACH_MESH_END \ + } \ + } + +/* Iterate over polygon and loop. */ +#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \ + elem_poly, index_poly, elem_loop, index_loop, params, mr) \ + CHECK_TYPE(params, const ExtractPolyMesh_Params *); \ + { \ + const MPoly *_mpoly = mr->mpoly; \ + const MLoop *_mloop = mr->mloop; \ + const int _poly_index_end = (params)->poly_range[1]; \ + for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \ + index_poly += 1) { \ + const MPoly *elem_poly = &_mpoly[index_poly]; \ + const int _index_end = elem_poly->loopstart + elem_poly->totloop; \ + for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \ + const MLoop *elem_loop = &_mloop[index_loop]; \ + (void)elem_loop; + +#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loose Edges + * \{ */ + +typedef struct ExtractLEdgeBMesh_Params { + const int *ledge; + int ledge_range[2]; +} ExtractLEdgeBMesh_Params; +typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr, + BMEdge *eed, + const int ledge_index, + void *data); + +#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \ + CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \ + BMEdge **_etable = mr->bm->etable; \ + const int *_ledge = (params)->ledge; \ + const int _ledge_index_end = (params)->ledge_range[1]; \ + for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ + index_ledge += 1) { \ + BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \ + (void)elem_edge; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LEDGE_FOREACH_BM_END \ + } \ + } \ + } + +typedef struct ExtractLEdgeMesh_Params { + const int *ledge; + int ledge_range[2]; +} ExtractLEdgeMesh_Params; +typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr, + const MEdge *med, + const uint ledge_index, + void *data); + +#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \ + CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \ + { \ + const MEdge *_medge = mr->medge; \ + const int *_ledge = (params)->ledge; \ + const int _ledge_index_end = (params)->ledge_range[1]; \ + for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \ + index_ledge += 1) { \ + const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \ + (void)elem_edge; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LEDGE_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract: Loose Vertices + * \{ */ + +typedef struct ExtractLVertBMesh_Params { + const int *lvert; + int lvert_range[2]; +} ExtractLVertBMesh_Params; +typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr, + BMVert *eve, + const int lvert_index, + void *data); + +#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \ + CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \ + { \ + BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \ + BMVert **vtable = mr->bm->vtable; \ + const int *lverts = (params)->lvert; \ + const int _lvert_index_end = (params)->lvert_range[1]; \ + for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ + index_lvert += 1) { \ + BMVert *elem_vert = vtable[lverts[index_lvert]]; \ + (void)elem_vert; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LVERT_FOREACH_BM_END \ + } \ + } \ + } + +typedef struct ExtractLVertMesh_Params { + const int *lvert; + int lvert_range[2]; +} ExtractLVertMesh_Params; +typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, + const MVert *mv, + const int lvert_index, + void *data); + +#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \ + CHECK_TYPE(params, const ExtractLVertMesh_Params *); \ + { \ + const MVert *mvert = mr->mvert; \ + const int *lverts = (params)->lvert; \ + const int _lvert_index_end = (params)->lvert_range[1]; \ + for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \ + index_lvert += 1) { \ + const MVert *elem = &mvert[lverts[index_lvert]]; \ + (void)elem; /* Quiet warning when unused. */ \ + { +#define EXTRACT_LVERT_FOREACH_MESH_END \ + } \ + } \ + } + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract Struct + * \{ */ + +typedef void *(ExtractInitFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer); +typedef void(ExtractFinishFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer, + void *data); + +typedef struct MeshExtract { + /** Executed on main thread and return user data for iteration functions. */ + ExtractInitFn *init; + /** Executed on one (or more if use_threading) worker thread(s). */ + ExtractTriBMeshFn *iter_looptri_bm; + ExtractTriMeshFn *iter_looptri_mesh; + ExtractPolyBMeshFn *iter_poly_bm; + ExtractPolyMeshFn *iter_poly_mesh; + ExtractLEdgeBMeshFn *iter_ledge_bm; + ExtractLEdgeMeshFn *iter_ledge_mesh; + ExtractLVertBMeshFn *iter_lvert_bm; + ExtractLVertMeshFn *iter_lvert_mesh; + /** Executed on one worker thread after all elements iterations. */ + ExtractFinishFn *finish; + /** Used to request common data. */ + const eMRDataType data_flag; + /** Used to know if the element callbacks are thread-safe and can be parallelized. */ + const bool use_threading; + /** + * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index + * buffer. + */ + const size_t mesh_buffer_offset; +} MeshExtract; + +/** \} */ + +/* draw_cache_extract_mesh_render_data.c */ +MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const DRW_MeshCDMask *cd_used, + const ToolSettings *ts, + const eMRIterType iter_type); +void mesh_render_data_free(MeshRenderData *mr); +void mesh_render_data_update_normals(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag); +void mesh_render_data_update_looptris(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag); + +/* draw_cache_extract_mesh_extractors.c */ +void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc); +eMRIterType mesh_extract_iter_type(const MeshExtract *ext); +const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, + const bool do_hq_normals, + const bool do_lines_loose_subbuffer); +/* + * Total number of extractions types. + */ +#define M_EXTRACT_LEN 38 + +extern const MeshExtract extract_tris; +extern const MeshExtract extract_lines; +extern const MeshExtract extract_lines_with_lines_loose; +extern const MeshExtract extract_points; +extern const MeshExtract extract_fdots; +extern const MeshExtract extract_lines_paint_mask; +extern const MeshExtract extract_lines_adjacency; +extern const MeshExtract extract_edituv_tris; +extern const MeshExtract extract_edituv_lines; +extern const MeshExtract extract_edituv_points; +extern const MeshExtract extract_edituv_fdots; +extern const MeshExtract extract_pos_nor; +extern const MeshExtract extract_pos_nor_hq; +extern const MeshExtract extract_lnor_hq; +extern const MeshExtract extract_lnor; +extern const MeshExtract extract_uv; +extern const MeshExtract extract_tan; +extern const MeshExtract extract_tan_hq; +extern const MeshExtract extract_sculpt_data; +extern const MeshExtract extract_vcol; +extern const MeshExtract extract_orco; +extern const MeshExtract extract_edge_fac; +extern const MeshExtract extract_weights; +extern const MeshExtract extract_edit_data; +extern const MeshExtract extract_edituv_data; +extern const MeshExtract extract_edituv_stretch_area; +extern const MeshExtract extract_edituv_stretch_angle; +extern const MeshExtract extract_mesh_analysis; +extern const MeshExtract extract_fdots_pos; +extern const MeshExtract extract_fdots_nor; +extern const MeshExtract extract_fdots_nor_hq; +extern const MeshExtract extract_fdots_uv; +extern const MeshExtract extract_fdots_edituv_data; +extern const MeshExtract extract_skin_roots; +extern const MeshExtract extract_poly_idx; +extern const MeshExtract extract_edge_idx; +extern const MeshExtract extract_vert_idx; +extern const MeshExtract extract_fdot_idx; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c new file mode 100644 index 00000000000..56b31a36fe5 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -0,0 +1,374 @@ +/* + * 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 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap.h" +#include "BLI_math.h" + +#include "BKE_editmesh.h" +#include "BKE_editmesh_cache.h" +#include "BKE_mesh.h" + +#include "GPU_batch.h" + +#include "ED_mesh.h" + +#include "draw_cache_extract_mesh_private.h" + +/* ---------------------------------------------------------------------- */ +/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data). + * \{ */ + +static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache) +{ + mr->ledges = cache->ledges; + mr->lverts = cache->lverts; + mr->vert_loose_len = cache->vert_loose_len; + mr->edge_loose_len = cache->edge_loose_len; + + mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2); +} + +static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr, + MeshBufferExtractionCache *cache) +{ + /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges + * and verts are calculated at the same time.*/ + if (cache->lverts) { + return; + } + + cache->vert_loose_len = 0; + cache->edge_loose_len = 0; + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + + BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); + + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + const MEdge *med = mr->medge; + for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { + if (med->flag & ME_LOOSEEDGE) { + cache->ledges[cache->edge_loose_len++] = med_index; + } + /* Tag verts as not loose. */ + BLI_BITMAP_ENABLE(lvert_map, med->v1); + BLI_BITMAP_ENABLE(lvert_map, med->v2); + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } + + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__); + for (int v = 0; v < mr->vert_len; v++) { + if (!BLI_BITMAP_TEST(lvert_map, v)) { + cache->lverts[cache->vert_loose_len++] = v; + } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + MEM_freeN(lvert_map); + } + else { + /* #BMesh */ + BMesh *bm = mr->bm; + int elem_id; + BMIter iter; + BMVert *eve; + BMEdge *ede; + + cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__); + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) { + if (eve->e == NULL) { + cache->lverts[cache->vert_loose_len++] = elem_id; + } + } + if (cache->vert_loose_len < mr->vert_len) { + cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts)); + } + + cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__); + BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) { + if (ede->l == NULL) { + cache->ledges[cache->edge_loose_len++] = elem_id; + } + } + if (cache->edge_loose_len < mr->edge_len) { + cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges)); + } + } +} + +/** + * Part of the creation of the #MeshRenderData that happens in a thread. + */ +void mesh_render_data_update_looptris(MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag) +{ + Mesh *me = mr->me; + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"); + BKE_mesh_recalc_looptri( + me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri); + } + } + else { + /* #BMesh */ + if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { + /* Edit mode ensures this is valid, no need to calculate. */ + BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL)); + } + } +} + +void mesh_render_data_update_normals(MeshRenderData *mr, + const eMRIterType UNUSED(iter_type), + const eMRDataType data_flag) +{ + Mesh *me = mr->me; + const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; + const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI; + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) { + mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__); + BKE_mesh_calc_normals_poly((MVert *)mr->mvert, + NULL, + mr->vert_len, + mr->mloop, + mr->mpoly, + mr->loop_len, + mr->poly_len, + mr->poly_normals, + true); + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL); + BKE_mesh_normals_loop_split(mr->me->mvert, + mr->vert_len, + mr->me->medge, + mr->edge_len, + mr->me->mloop, + mr->loop_normals, + mr->loop_len, + mr->me->mpoly, + mr->poly_normals, + mr->poly_len, + is_auto_smooth, + split_angle, + NULL, + clnors, + NULL); + } + } + else { + /* #BMesh */ + if (data_flag & MR_DATA_POLY_NOR) { + /* Use #BMFace.no instead. */ + } + if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) { + + const float(*vert_coords)[3] = NULL; + const float(*vert_normals)[3] = NULL; + const float(*poly_normals)[3] = NULL; + + if (mr->edit_data && mr->edit_data->vertexCos) { + vert_coords = mr->bm_vert_coords; + vert_normals = mr->bm_vert_normals; + poly_normals = mr->bm_poly_normals; + } + + mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__); + const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL); + BM_loops_calc_normal_vcos(mr->bm, + vert_coords, + vert_normals, + poly_normals, + is_auto_smooth, + split_angle, + mr->loop_normals, + NULL, + NULL, + clnors_offset, + false); + } + } +} + +/** + * \param is_mode_active: When true, use the modifiers from the edit-data, + * otherwise don't use modifiers as they are not from this object. + */ +MeshRenderData *mesh_render_data_create(Mesh *me, + MeshBufferExtractionCache *cache, + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const DRW_MeshCDMask *UNUSED(cd_used), + const ToolSettings *ts, + const eMRIterType iter_type) +{ + MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); + mr->toolsettings = ts; + mr->mat_len = mesh_render_mat_len_get(me); + + copy_m4_m4(mr->obmat, obmat); + + if (is_editmode) { + BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final); + mr->bm = me->edit_mesh->bm; + mr->edit_bmesh = me->edit_mesh; + mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; + mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; + + if (mr->edit_data) { + EditMeshData *emd = mr->edit_data; + if (emd->vertexCos) { + BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd); + BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd); + } + + mr->bm_vert_coords = mr->edit_data->vertexCos; + mr->bm_vert_normals = mr->edit_data->vertexNos; + mr->bm_poly_normals = mr->edit_data->polyNos; + mr->bm_poly_centers = mr->edit_data->polyCos; + } + + bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + bool use_mapped = is_mode_active && + (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); + + int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; + + BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); + BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); + + mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); + mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); + mr->eed_act = BM_mesh_active_edge_get(mr->bm); + mr->eve_act = BM_mesh_active_vert_get(mr->bm); + + mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE); + mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT); +#ifdef WITH_FREESTYLE + mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); + mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); +#endif + + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH; + + /* Seems like the mesh_eval_final do not have the right origin indices. + * Force not mapped in this case. */ + if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) { + // mr->edit_bmesh = NULL; + mr->extract_type = MR_EXTRACT_MESH; + } + } + else { + mr->me = me; + mr->edit_bmesh = NULL; + + bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original; + if (use_mapped) { + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + + use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex); + } + + mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH; + } + + if (mr->extract_type != MR_EXTRACT_BMESH) { + /* Mesh */ + mr->vert_len = mr->me->totvert; + mr->edge_len = mr->me->totedge; + mr->loop_len = mr->me->totloop; + mr->poly_len = mr->me->totpoly; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + + mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT); + mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE); + mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP); + mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY); + + mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX); + mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX); + mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX); + } + else { + /* #BMesh */ + BMesh *bm = mr->bm; + + mr->vert_len = bm->totvert; + mr->edge_len = bm->totedge; + mr->loop_len = bm->totloop; + mr->poly_len = bm->totface; + mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); + } + + if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { + mesh_render_data_loose_geom_ensure(mr, cache); + mesh_render_data_loose_geom_load(mr, cache); + } + + return mr; +} + +void mesh_render_data_free(MeshRenderData *mr) +{ + MEM_SAFE_FREE(mr->mlooptri); + MEM_SAFE_FREE(mr->poly_normals); + MEM_SAFE_FREE(mr->loop_normals); + + /* Loose geometry are owned by MeshBufferExtractionCache. */ + mr->ledges = NULL; + mr->lverts = NULL; + + MEM_freeN(mr); +} + +/** \} */ \ No newline at end of file diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 4de97411059..ea3d921f2c5 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -42,6 +42,7 @@ struct SpaceImage; struct ToolSettings; struct ViewLayer; struct bNode; +struct bNodeTree; struct wmKeyConfig; /* uvedit_ops.c */ -- cgit v1.2.3 From 9adfd278f7487798f1b0124c7e44cf9934b4ba54 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Tue, 1 Jun 2021 10:25:38 +0200 Subject: Compositor: Full-frame base system This patch adds the base code needed to make the full-frame system work for both current tiled/per-pixel implementation of operations and full-frame. Two execution models: - Tiled: Current implementation. Renders execution groups in tiles from outputs to input. Not all operations are buffered. Runs the tiled/per-pixel implementation. - FullFrame: All operations are buffered. Fully renders operations from inputs to outputs. Runs full-frame implementation of operations if available otherwise the current tiled/per-pixel. Creates output buffers on first read and free them as soon as all its readers have finished, reducing peak memory usage of complex/long trees. Operations are multi-threaded but do not run in parallel as Tiled (will be done in another patch). This should allow us to convert operations to full-frame in small steps with the system already working and solve the problem of high memory usage. FullFrame breaking changes respect Tiled system, mainly: - Translate, Rotate, Scale, and Transform take effect immediately instead of next buffered operation. - Any sampling is always done over inputs instead of last buffered operation. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D11113 --- release/scripts/startup/bl_ui/space_node.py | 4 + release/scripts/startup/bl_ui/space_userpref.py | 1 + source/blender/compositor/CMakeLists.txt | 12 + source/blender/compositor/COM_defines.h | 10 + .../compositor/intern/COM_BufferOperation.cc | 65 ++++ .../compositor/intern/COM_BufferOperation.h | 37 +++ source/blender/compositor/intern/COM_CPUDevice.cc | 23 +- .../compositor/intern/COM_CompositorContext.cc | 18 ++ .../compositor/intern/COM_CompositorContext.h | 5 + source/blender/compositor/intern/COM_Debug.cc | 37 ++- source/blender/compositor/intern/COM_Debug.h | 2 +- source/blender/compositor/intern/COM_Enums.h | 15 + .../compositor/intern/COM_ExecutionGroup.cc | 1 + .../compositor/intern/COM_ExecutionModel.cc | 48 +++ .../blender/compositor/intern/COM_ExecutionModel.h | 84 ++++++ .../compositor/intern/COM_ExecutionSystem.cc | 143 ++------- .../compositor/intern/COM_ExecutionSystem.h | 25 +- .../intern/COM_FullFrameExecutionModel.cc | 328 +++++++++++++++++++++ .../intern/COM_FullFrameExecutionModel.h | 89 ++++++ .../intern/COM_MultiThreadedOperation.cc | 26 ++ .../compositor/intern/COM_MultiThreadedOperation.h | 73 +++++ .../blender/compositor/intern/COM_NodeOperation.cc | 176 +++++++++++ .../blender/compositor/intern/COM_NodeOperation.h | 66 +++++ .../compositor/intern/COM_NodeOperationBuilder.cc | 12 +- .../intern/COM_SharedOperationBuffers.cc | 131 ++++++++ .../compositor/intern/COM_SharedOperationBuffers.h | 71 +++++ .../compositor/intern/COM_TiledExecutionModel.cc | 158 ++++++++++ .../compositor/intern/COM_TiledExecutionModel.h | 54 ++++ source/blender/compositor/intern/COM_WorkPackage.h | 13 + .../blender/compositor/intern/COM_WorkScheduler.cc | 13 +- .../blender/compositor/intern/COM_WorkScheduler.h | 2 + .../blender/compositor/nodes/COM_TranslateNode.cc | 5 +- source/blender/makesdna/DNA_node_types.h | 9 +- source/blender/makesdna/DNA_userdef_types.h | 3 +- source/blender/makesrna/intern/rna_nodetree.c | 20 ++ source/blender/makesrna/intern/rna_userdef.c | 8 + 36 files changed, 1629 insertions(+), 158 deletions(-) create mode 100644 source/blender/compositor/intern/COM_BufferOperation.cc create mode 100644 source/blender/compositor/intern/COM_BufferOperation.h create mode 100644 source/blender/compositor/intern/COM_ExecutionModel.cc create mode 100644 source/blender/compositor/intern/COM_ExecutionModel.h create mode 100644 source/blender/compositor/intern/COM_FullFrameExecutionModel.cc create mode 100644 source/blender/compositor/intern/COM_FullFrameExecutionModel.h create mode 100644 source/blender/compositor/intern/COM_MultiThreadedOperation.cc create mode 100644 source/blender/compositor/intern/COM_MultiThreadedOperation.h create mode 100644 source/blender/compositor/intern/COM_SharedOperationBuffers.cc create mode 100644 source/blender/compositor/intern/COM_SharedOperationBuffers.h create mode 100644 source/blender/compositor/intern/COM_TiledExecutionModel.cc create mode 100644 source/blender/compositor/intern/COM_TiledExecutionModel.h diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 89ce742b81e..1208ca0a64a 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -660,8 +660,12 @@ class NODE_PT_quality(bpy.types.Panel): snode = context.space_data tree = snode.node_tree + prefs = bpy.context.preferences col = layout.column() + if prefs.experimental.use_full_frame_compositor: + col.prop(tree, "execution_mode") + col.prop(tree, "render_quality", text="Render") col.prop(tree, "edit_quality", text="Edit") col.prop(tree, "chunk_size") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index de78b88c0f6..d85fe16d654 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2256,6 +2256,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): context, ( ({"property": "use_new_hair_type"}, "T68981"), ({"property": "use_new_point_cloud_type"}, "T75717"), + ({"property": "use_full_frame_compositor"}, "T88150"), ), ) diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 65391794c12..ac59d832013 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -49,6 +49,8 @@ set(SRC COM_compositor.h COM_defines.h + intern/COM_BufferOperation.cc + intern/COM_BufferOperation.h intern/COM_CPUDevice.cc intern/COM_CPUDevice.h intern/COM_ChunkOrder.cc @@ -66,14 +68,20 @@ set(SRC intern/COM_Enums.cc intern/COM_ExecutionGroup.cc intern/COM_ExecutionGroup.h + intern/COM_ExecutionModel.cc + intern/COM_ExecutionModel.h intern/COM_ExecutionSystem.cc intern/COM_ExecutionSystem.h + intern/COM_FullFrameExecutionModel.cc + intern/COM_FullFrameExecutionModel.h intern/COM_MemoryBuffer.cc intern/COM_MemoryBuffer.h intern/COM_MemoryProxy.cc intern/COM_MemoryProxy.h intern/COM_MetaData.cc intern/COM_MetaData.h + intern/COM_MultiThreadedOperation.cc + intern/COM_MultiThreadedOperation.h intern/COM_Node.cc intern/COM_Node.h intern/COM_NodeConverter.cc @@ -86,8 +94,12 @@ set(SRC intern/COM_NodeOperationBuilder.h intern/COM_OpenCLDevice.cc intern/COM_OpenCLDevice.h + intern/COM_SharedOperationBuffers.cc + intern/COM_SharedOperationBuffers.h intern/COM_SingleThreadedOperation.cc intern/COM_SingleThreadedOperation.h + intern/COM_TiledExecutionModel.cc + intern/COM_TiledExecutionModel.h intern/COM_WorkPackage.cc intern/COM_WorkPackage.h intern/COM_WorkScheduler.cc diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index ef889807030..5a52d216117 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -20,6 +20,16 @@ namespace blender::compositor { +enum class eExecutionModel { + /** + * Operations are executed from outputs to inputs grouped in execution groups and rendered + * in tiles. + */ + Tiled, + /** Operations are fully rendered in order from inputs to outputs. */ + FullFrame +}; + /** * \brief possible data types for sockets * \ingroup Model diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc new file mode 100644 index 00000000000..c1e64142443 --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -0,0 +1,65 @@ +/* + * 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_BufferOperation.h" + +namespace blender::compositor { + +BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) : NodeOperation() +{ + buffer_ = buffer; + /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following + * code to: set_resolution(buffer.get_size()) */ + unsigned int resolution[2]; + resolution[0] = buffer->getWidth(); + resolution[1] = buffer->getHeight(); + setResolution(resolution); + addOutputSocket(data_type); +} + +void *BufferOperation::initializeTileData(rcti * /*rect*/) +{ + return buffer_; +} + +void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +{ + switch (sampler) { + case PixelSampler::Nearest: + buffer_->read(output, x, y); + break; + case PixelSampler::Bilinear: + default: + buffer_->readBilinear(output, x, y); + break; + case PixelSampler::Bicubic: + /* No bicubic. Same implementation as ReadBufferOperation. */ + buffer_->readBilinear(output, x, y); + break; + } +} + +void BufferOperation::executePixelFiltered( + float output[4], float x, float y, float dx[2], float dy[2]) +{ + const float uv[2] = {x, y}; + const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; + buffer_->readEWA(output, uv, deriv); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h new file mode 100644 index 00000000000..f87cd4db94e --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferOperation.h @@ -0,0 +1,37 @@ +/* + * 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_NodeOperation.h" + +namespace blender::compositor { + +class BufferOperation : public NodeOperation { + private: + MemoryBuffer *buffer_; + + public: + BufferOperation(MemoryBuffer *buffer, DataType data_type); + + void *initializeTileData(rcti *rect) override; + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc index 29a82bec636..2ca5557e278 100644 --- a/source/blender/compositor/intern/COM_CPUDevice.cc +++ b/source/blender/compositor/intern/COM_CPUDevice.cc @@ -30,11 +30,24 @@ CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id) void CPUDevice::execute(WorkPackage *work_package) { - const unsigned int chunkNumber = work_package->chunk_number; - ExecutionGroup *executionGroup = work_package->execution_group; - - executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber); - executionGroup->finalizeChunkExecution(chunkNumber, nullptr); + switch (work_package->type) { + case eWorkPackageType::Tile: { + const unsigned int chunkNumber = work_package->chunk_number; + ExecutionGroup *executionGroup = work_package->execution_group; + + executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber); + executionGroup->finalizeChunkExecution(chunkNumber, nullptr); + break; + } + case eWorkPackageType::CustomFunction: { + work_package->execute_fn(); + break; + } + } + + if (work_package->executed_fn) { + work_package->executed_fn(); + } } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index f70f3a8ebfc..61e299c045e 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -21,6 +21,7 @@ #include #include "BLI_assert.h" +#include "DNA_userdef_types.h" namespace blender::compositor { @@ -33,6 +34,7 @@ CompositorContext::CompositorContext() this->m_fastCalculation = false; this->m_viewSettings = nullptr; this->m_displaySettings = nullptr; + this->m_bnodetree = nullptr; } int CompositorContext::getFramenumber() const @@ -41,4 +43,20 @@ int CompositorContext::getFramenumber() const return m_rd->cfra; } +eExecutionModel CompositorContext::get_execution_model() const +{ + if (U.experimental.use_full_frame_compositor) { + BLI_assert(m_bnodetree != nullptr); + switch (m_bnodetree->execution_mode) { + case 1: + return eExecutionModel::FullFrame; + case 0: + return eExecutionModel::Tiled; + default: + BLI_assert(!"Invalid execution mode"); + } + } + return eExecutionModel::Tiled; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index e6164246bdd..56251511576 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -281,6 +281,11 @@ class CompositorContext { { return m_rd->size * 0.01f; } + + /** + * Get active execution model. + */ + eExecutionModel get_execution_model() const; }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index dfb4f53fee5..4cf7e09a7d8 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -211,12 +211,14 @@ int DebugInfo::graphviz_legend_group( return len; } -int DebugInfo::graphviz_legend(char *str, int maxlen) +int DebugInfo::graphviz_legend(char *str, int maxlen, const bool has_execution_groups) { int len = 0; len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{\r\n"); - len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n"); + if (has_execution_groups) { + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n"); + } len += snprintf( str + len, maxlen > len ? maxlen - len : 0, "Legend [shape=none, margin=0, label=<\r\n"); @@ -236,21 +238,24 @@ int DebugInfo::graphviz_legend(char *str, int maxlen) "Viewer", "lightskyblue3", str + len, maxlen > len ? maxlen - len : 0); len += graphviz_legend_color( "Active Viewer", "lightskyblue1", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_color( - "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_color( - "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0); + if (has_execution_groups) { + len += graphviz_legend_color( + "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_color( + "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0); + } len += graphviz_legend_color( "Input Value", "khaki1", str + len, maxlen > len ? maxlen - len : 0); - len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n"); - - len += graphviz_legend_group( - "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_group( - "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0); - len += graphviz_legend_group( - "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0); + if (has_execution_groups) { + len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n"); + len += graphviz_legend_group( + "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_group( + "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_legend_group( + "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0); + } len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n"); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, ">];\r\n"); @@ -387,7 +392,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma } } - len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0); + const bool has_execution_groups = system->getContext().get_execution_model() == + eExecutionModel::Tiled; + 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_Debug.h b/source/blender/compositor/intern/COM_Debug.h index e1aea69e481..0de3a5e39dc 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -129,7 +129,7 @@ class DebugInfo { const char *name, const char *color, const char *style, char *str, int maxlen); static int graphviz_legend_group( const char *name, const char *color, const char *style, char *str, int maxlen); - static int graphviz_legend(char *str, int maxlen); + static int graphviz_legend(char *str, int maxlen, bool has_execution_groups); static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen); }; diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h index f65ce3e856e..519e7df940e 100644 --- a/source/blender/compositor/intern/COM_Enums.h +++ b/source/blender/compositor/intern/COM_Enums.h @@ -70,6 +70,21 @@ enum class eWorkPackageState { Executed = 2, }; +/** + * \brief Work type to execute. + * \ingroup Execution + */ +enum class eWorkPackageType { + /** + * \brief Executes an execution group tile. + */ + Tile = 0, + /** + * \brief Executes a custom function. + */ + CustomFunction = 1 +}; + std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority); std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state); diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc index 80d453bf7f9..68bda8c70d6 100644 --- a/source/blender/compositor/intern/COM_ExecutionGroup.cc +++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc @@ -157,6 +157,7 @@ void ExecutionGroup::init_work_packages() if (this->m_chunks_len != 0) { m_work_packages.resize(this->m_chunks_len); for (unsigned int index = 0; index < m_chunks_len; index++) { + m_work_packages[index].type = eWorkPackageType::Tile; m_work_packages[index].state = eWorkPackageState::NotScheduled; m_work_packages[index].execution_group = this; m_work_packages[index].chunk_number = index; diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc new file mode 100644 index 00000000000..4d7f62e091b --- /dev/null +++ b/source/blender/compositor/intern/COM_ExecutionModel.cc @@ -0,0 +1,48 @@ +/* + * 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_ExecutionModel.h" + +namespace blender::compositor { + +ExecutionModel::ExecutionModel(CompositorContext &context, Span operations) + : context_(context), operations_(operations) +{ + const bNodeTree *node_tree = context_.getbNodeTree(); + + const rctf *viewer_border = &node_tree->viewer_border; + border_.use_viewer_border = (node_tree->flag & NTREE_VIEWER_BORDER) && + viewer_border->xmin < viewer_border->xmax && + viewer_border->ymin < viewer_border->ymax; + border_.viewer_border = viewer_border; + + const RenderData *rd = context_.getRenderData(); + /* Case when cropping to render border happens is handled in + * compositor output and render layer nodes. */ + border_.use_render_border = context.isRendering() && (rd->mode & R_BORDER) && + !(rd->mode & R_CROP); + border_.render_border = &rd->border; +} + +bool ExecutionModel::is_breaked() const +{ + const bNodeTree *btree = context_.getbNodeTree(); + return btree->test_break(btree->tbh); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h new file mode 100644 index 00000000000..9e8466b9282 --- /dev/null +++ b/source/blender/compositor/intern/COM_ExecutionModel.h @@ -0,0 +1,84 @@ +/* + * 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 "BLI_rect.h" +#include "BLI_vector.hh" + +#include "COM_ExecutionSystem.h" + +#include + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +class NodeOperation; + +/** + * Base class for execution models. Contains shared implementation. + */ +class ExecutionModel { + protected: + /** + * Render and viewer border info. Coordinates are normalized. + */ + struct { + bool use_render_border; + const rctf *render_border; + bool use_viewer_border; + const rctf *viewer_border; + } border_; + + /** + * Context used during execution. + */ + CompositorContext &context_; + + /** + * All operations being executed. + */ + Span operations_; + + public: + ExecutionModel(CompositorContext &context, Span operations); + + virtual ~ExecutionModel() + { + } + + virtual void execute(ExecutionSystem &exec_system) = 0; + + virtual void execute_work(const rcti &UNUSED(work_rect), + std::function UNUSED(work_func)) + { + BLI_assert(!"Method not supported by current execution model"); + } + + protected: + bool is_breaked() const; + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:BaseExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index e22dc17837b..a12ec774032 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -21,16 +21,11 @@ #include "BLI_utildefines.h" #include "PIL_time.h" -#include "BKE_node.h" - -#include "BLT_translation.h" - -#include "COM_Converter.h" #include "COM_Debug.h" -#include "COM_ExecutionGroup.h" +#include "COM_FullFrameExecutionModel.h" #include "COM_NodeOperation.h" #include "COM_NodeOperationBuilder.h" -#include "COM_ReadBufferOperation.h" +#include "COM_TiledExecutionModel.h" #include "COM_WorkScheduler.h" #ifdef WITH_CXX_GUARDEDALLOC @@ -73,41 +68,23 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, builder.convertToOperations(this); } - unsigned int resolution[2]; - - rctf *viewer_border = &editingtree->viewer_border; - bool use_viewer_border = (editingtree->flag & NTREE_VIEWER_BORDER) && - viewer_border->xmin < viewer_border->xmax && - viewer_border->ymin < viewer_border->ymax; - - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Determining resolution")); - - for (ExecutionGroup *executionGroup : m_groups) { - resolution[0] = 0; - resolution[1] = 0; - executionGroup->determineResolution(resolution); - - if (rendering) { - /* case when cropping to render border happens is handled in - * compositor output and render layer nodes - */ - if ((rd->mode & R_BORDER) && !(rd->mode & R_CROP)) { - executionGroup->setRenderBorder( - rd->border.xmin, rd->border.xmax, rd->border.ymin, rd->border.ymax); - } - } - - if (use_viewer_border) { - executionGroup->setViewerBorder( - viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); - } + switch (m_context.get_execution_model()) { + case eExecutionModel::Tiled: + execution_model_ = new TiledExecutionModel(m_context, m_operations, m_groups); + break; + case eExecutionModel::FullFrame: + execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations); + break; + default: + BLI_assert(!"Non implemented execution model"); + break; } - - // DebugInfo::graphviz(this); } ExecutionSystem::~ExecutionSystem() { + delete execution_model_; + for (NodeOperation *operation : m_operations) { delete operation; } @@ -126,100 +103,16 @@ void ExecutionSystem::set_operations(const Vector &operations, m_groups = groups; } -static void update_read_buffer_offset(Vector &operations) -{ - unsigned int order = 0; - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; - readOperation->setOffset(order); - order++; - } - } -} - -static void init_write_operations_for_execution(Vector &operations, - const bNodeTree *bTree) -{ - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); - } - } -} - -static void link_write_buffers(Vector &operations) -{ - for (NodeOperation *operation : operations) { - if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = static_cast(operation); - readOperation->updateMemoryBuffer(); - } - } -} - -static void init_non_write_operations_for_execution(Vector &operations, - const bNodeTree *bTree) -{ - for (NodeOperation *operation : operations) { - if (!operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); - } - } -} - -static void init_execution_groups_for_execution(Vector &groups, - const int chunk_size) -{ - for (ExecutionGroup *execution_group : groups) { - execution_group->setChunksize(chunk_size); - execution_group->initExecution(); - } -} - void ExecutionSystem::execute() { - const bNodeTree *editingtree = this->m_context.getbNodeTree(); - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); - DebugInfo::execute_started(this); - update_read_buffer_offset(m_operations); - - init_write_operations_for_execution(m_operations, m_context.getbNodeTree()); - link_write_buffers(m_operations); - init_non_write_operations_for_execution(m_operations, m_context.getbNodeTree()); - init_execution_groups_for_execution(m_groups, m_context.getChunksize()); - - WorkScheduler::start(this->m_context); - execute_groups(eCompositorPriority::High); - if (!this->getContext().isFastCalculation()) { - execute_groups(eCompositorPriority::Medium); - execute_groups(eCompositorPriority::Low); - } - WorkScheduler::finish(); - WorkScheduler::stop(); - - editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); - - for (NodeOperation *operation : m_operations) { - operation->deinitExecution(); - } - - for (ExecutionGroup *execution_group : m_groups) { - execution_group->deinitExecution(); - } + execution_model_->execute(*this); } -void ExecutionSystem::execute_groups(eCompositorPriority priority) +void ExecutionSystem::execute_work(const rcti &work_rect, + std::function work_func) { - for (ExecutionGroup *execution_group : m_groups) { - if (execution_group->get_flags().is_output && - execution_group->getRenderPriority() == priority) { - execution_group->execute(this); - } - } + execution_model_->execute_work(work_rect, work_func); } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index e6170c48778..e106209651c 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -25,6 +25,7 @@ class ExecutionGroup; #include "COM_ExecutionGroup.h" #include "COM_Node.h" #include "COM_NodeOperation.h" +#include "COM_SharedOperationBuffers.h" #include "DNA_color_types.h" #include "DNA_node_types.h" @@ -115,12 +116,20 @@ namespace blender::compositor { * \see ExecutionGroup class representing the ExecutionGroup */ +/* Forward declarations. */ +class ExecutionModel; + /** * \brief the ExecutionSystem contains the whole compositor tree. */ class ExecutionSystem { - private: + /** + * Contains operations active buffers data. Buffers will be disposed once reader operations are + * finished. + */ + SharedOperationBuffers active_buffers_; + /** * \brief the context used during execution */ @@ -136,6 +145,11 @@ class ExecutionSystem { */ Vector m_groups; + /** + * Active execution model implementation. + */ + ExecutionModel *execution_model_; + private: // methods public: /** @@ -178,9 +192,14 @@ class ExecutionSystem { return this->m_context; } - private: - void execute_groups(eCompositorPriority priority); + SharedOperationBuffers &get_active_buffers() + { + return active_buffers_; + } + + void execute_work(const rcti &work_rect, std::function work_func); + private: /* allow the DebugInfo class to look at internals */ friend class DebugInfo; diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc new file mode 100644 index 00000000000..1099aadd89d --- /dev/null +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -0,0 +1,328 @@ +/* + * 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_FullFrameExecutionModel.h" +#include "COM_Debug.h" +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" +#include "COM_WorkScheduler.h" + +#include "BLT_translation.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, + SharedOperationBuffers &shared_buffers, + Span operations) + : ExecutionModel(context, operations), + active_buffers_(shared_buffers), + num_operations_finished_(0), + priorities_(), + work_mutex_(), + work_finished_cond_() +{ + priorities_.append(eCompositorPriority::High); + if (!context.isFastCalculation()) { + priorities_.append(eCompositorPriority::Medium); + priorities_.append(eCompositorPriority::Low); + } + + BLI_mutex_init(&work_mutex_); + BLI_condition_init(&work_finished_cond_); +} + +FullFrameExecutionModel::~FullFrameExecutionModel() +{ + BLI_condition_end(&work_finished_cond_); + BLI_mutex_end(&work_mutex_); +} + +void FullFrameExecutionModel::execute(ExecutionSystem &exec_system) +{ + const bNodeTree *node_tree = this->context_.getbNodeTree(); + node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution")); + + DebugInfo::graphviz(&exec_system); + + determine_areas_to_render_and_reads(); + render_operations(exec_system); +} + +void FullFrameExecutionModel::determine_areas_to_render_and_reads() +{ + const bool is_rendering = context_.isRendering(); + const bNodeTree *node_tree = context_.getbNodeTree(); + + rcti area; + for (eCompositorPriority priority : priorities_) { + for (NodeOperation *op : operations_) { + op->setbNodeTree(node_tree); + if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + get_output_render_area(op, area); + determine_areas_to_render(op, area); + determine_reads(op); + } + } + } +} + +void FullFrameExecutionModel::ensure_inputs_rendered(NodeOperation *op, + ExecutionSystem &exec_system) +{ + const int num_inputs = op->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = op->get_input_operation(i); + if (!active_buffers_.is_operation_rendered(input_op)) { + render_operation(input_op, exec_system); + } + } +} + +Vector FullFrameExecutionModel::get_input_buffers(NodeOperation *op) +{ + const int num_inputs = op->getNumberOfInputSockets(); + Vector inputs_buffers(num_inputs); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = op->get_input_operation(i); + inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op); + } + return inputs_buffers; +} + +MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op) +{ + rcti op_rect; + BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight()); + + const DataType data_type = op->getOutputSocket(0)->getDataType(); + /* TODO: We should check if the operation is constant instead of is_set_operation. Finding a way + * to know if an operation is constant has to be implemented yet. */ + const bool is_a_single_elem = op->get_flags().is_set_operation; + return new MemoryBuffer(data_type, op_rect, is_a_single_elem); +} + +void FullFrameExecutionModel::render_operation(NodeOperation *op, ExecutionSystem &exec_system) +{ + if (active_buffers_.is_operation_rendered(op)) { + return; + } + + ensure_inputs_rendered(op, exec_system); + Vector input_bufs = get_input_buffers(op); + + const bool has_outputs = op->getNumberOfOutputSockets() > 0; + MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr; + Span areas = active_buffers_.get_areas_to_render(op); + op->render(op_buf, areas, input_bufs, exec_system); + active_buffers_.set_rendered_buffer(op, std::unique_ptr(op_buf)); + + operation_finished(op); +} + +/** + * Render output operations in order of priority. + */ +void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system) +{ + const bool is_rendering = context_.isRendering(); + + WorkScheduler::start(this->context_); + for (eCompositorPriority priority : priorities_) { + for (NodeOperation *op : operations_) { + if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + render_operation(op, exec_system); + } + } + } + WorkScheduler::stop(); +} + +/** + * Determines all input operations areas needed to render given operation area. + * \param operation: Renderer operation. + * \param render_area: Area within given operation bounds to render. + */ +void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *operation, + const rcti &render_area) +{ + if (active_buffers_.is_area_registered(operation, render_area)) { + return; + } + + active_buffers_.register_area(operation, render_area); + + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = operation->get_input_operation(i); + rcti input_op_rect, input_area; + BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight()); + operation->get_area_of_interest(input_op, render_area, input_area); + + /* Ensure area of interest is within operation bounds, cropping areas outside. */ + BLI_rcti_isect(&input_area, &input_op_rect, &input_area); + + determine_areas_to_render(input_op, input_area); + } +} + +/** + * Determines the reads given operation and its inputs will receive (i.e: Number of dependent + * operations each operation has). + */ +void FullFrameExecutionModel::determine_reads(NodeOperation *operation) +{ + if (active_buffers_.has_registered_reads(operation)) { + return; + } + + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + NodeOperation *input_op = operation->get_input_operation(i); + determine_reads(input_op); + active_buffers_.register_read(input_op); + } +} + +/** + * Calculates given output operation area to be rendered taking into account viewer and render + * borders. + */ +void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area) +{ + BLI_assert(output_op->isOutputOperation(context_.isRendering())); + + /* By default return operation bounds (no border). */ + const int op_width = output_op->getWidth(); + const int op_height = output_op->getHeight(); + BLI_rcti_init(&r_area, 0, op_width, 0, op_height); + + const bool has_viewer_border = border_.use_viewer_border && + (output_op->get_flags().is_viewer_operation || + output_op->get_flags().is_preview_operation); + const bool has_render_border = border_.use_render_border; + if (has_viewer_border || has_render_border) { + /* Get border with normalized coordinates. */ + const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border; + + /* Return de-normalized border. */ + BLI_rcti_init(&r_area, + norm_border->xmin * op_width, + norm_border->xmax * op_width, + norm_border->ymin * op_height, + norm_border->ymax * op_height); + } +} + +/** + * Multi-threadedly execute given work function passing work_rect splits as argument. + */ +void FullFrameExecutionModel::execute_work(const rcti &work_rect, + std::function work_func) +{ + if (is_breaked()) { + return; + } + + /* Split work vertically to maximize continuous memory. */ + const int work_height = BLI_rcti_size_y(&work_rect); + const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height); + const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works; + int remaining_height = work_height - split_height * num_sub_works; + + Vector sub_works(num_sub_works); + int sub_work_y = work_rect.ymin; + int num_sub_works_finished = 0; + for (int i = 0; i < num_sub_works; i++) { + int sub_work_height = split_height; + + /* Distribute remaining height between sub-works. */ + if (remaining_height > 0) { + sub_work_height++; + remaining_height--; + } + + WorkPackage &sub_work = sub_works[i]; + sub_work.type = eWorkPackageType::CustomFunction; + sub_work.execute_fn = [=, &work_func, &work_rect]() { + if (is_breaked()) { + return; + } + rcti split_rect; + BLI_rcti_init( + &split_rect, work_rect.xmin, work_rect.xmax, sub_work_y, sub_work_y + sub_work_height); + work_func(split_rect); + }; + sub_work.executed_fn = [&]() { + BLI_mutex_lock(&work_mutex_); + num_sub_works_finished++; + if (num_sub_works_finished == num_sub_works) { + BLI_condition_notify_one(&work_finished_cond_); + } + BLI_mutex_unlock(&work_mutex_); + }; + WorkScheduler::schedule(&sub_work); + sub_work_y += sub_work_height; + } + BLI_assert(sub_work_y == work_rect.ymax); + + WorkScheduler::finish(); + + /* Ensure all sub-works finished. + * TODO: This a workaround for WorkScheduler::finish() not waiting all works on queue threading + * model. Sync code should be removed once it's fixed. */ + BLI_mutex_lock(&work_mutex_); + if (num_sub_works_finished < num_sub_works) { + BLI_condition_wait(&work_finished_cond_, &work_mutex_); + } + BLI_mutex_unlock(&work_mutex_); +} + +void FullFrameExecutionModel::operation_finished(NodeOperation *operation) +{ + /* Report inputs reads so that buffers may be freed/reused. */ + const int num_inputs = operation->getNumberOfInputSockets(); + for (int i = 0; i < num_inputs; i++) { + active_buffers_.read_finished(operation->get_input_operation(i)); + } + + num_operations_finished_++; + update_progress_bar(); +} + +void FullFrameExecutionModel::update_progress_bar() +{ + const bNodeTree *tree = context_.getbNodeTree(); + if (tree) { + const float progress = num_operations_finished_ / static_cast(operations_.size()); + tree->progress(tree->prh, progress); + + char buf[128]; + BLI_snprintf(buf, + sizeof(buf), + TIP_("Compositing | Operation %i-%li"), + num_operations_finished_ + 1, + operations_.size()); + tree->stats_draw(tree->sdh, buf); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h new file mode 100644 index 00000000000..2c0d5e0460a --- /dev/null +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h @@ -0,0 +1,89 @@ +/* + * 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_ExecutionModel.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +/* Forward declarations. */ +class ExecutionGroup; + +/** + * Fully renders operations in order from inputs to outputs. + */ +class FullFrameExecutionModel : public ExecutionModel { + private: + /** + * Contains operations active buffers data. Buffers will be disposed once reader operations are + * finished. + */ + SharedOperationBuffers &active_buffers_; + + /** + * Number of operations finished. + */ + int num_operations_finished_; + + /** + * Order of priorities for output operations execution. + */ + Vector priorities_; + + ThreadMutex work_mutex_; + ThreadCondition work_finished_cond_; + + public: + FullFrameExecutionModel(CompositorContext &context, + SharedOperationBuffers &shared_buffers, + Span operations); + ~FullFrameExecutionModel(); + + void execute(ExecutionSystem &exec_system) override; + + void execute_work(const rcti &work_rect, + std::function work_func) override; + + private: + void determine_areas_to_render_and_reads(); + void render_operations(ExecutionSystem &exec_system); + + void ensure_inputs_rendered(NodeOperation *op, ExecutionSystem &exec_system); + Vector get_input_buffers(NodeOperation *op); + MemoryBuffer *create_operation_buffer(NodeOperation *op); + void render_operation(NodeOperation *op, ExecutionSystem &exec_system); + + void operation_finished(NodeOperation *operation); + + void get_output_render_area(NodeOperation *output_op, rcti &r_area); + void determine_areas_to_render(NodeOperation *operation, const rcti &render_area); + void determine_reads(NodeOperation *operation); + + void update_progress_bar(); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:FullFrameExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc new file mode 100644 index 00000000000..c54c2edccb0 --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc @@ -0,0 +1,26 @@ +#include "COM_MultiThreadedOperation.h" +#include "COM_ExecutionSystem.h" + +namespace blender::compositor { + +MultiThreadedOperation::MultiThreadedOperation() +{ + m_num_passes = 1; + flags.is_fullframe_operation = true; +} + +void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &output_area, + blender::Span inputs, + ExecutionSystem &exec_system) +{ + for (int current_pass = 0; current_pass < m_num_passes; current_pass++) { + update_memory_buffer_started(output, output_area, inputs, exec_system, current_pass); + exec_system.execute_work(output_area, [=, &exec_system](const rcti &split_rect) { + update_memory_buffer_partial(output, split_rect, inputs, exec_system, current_pass); + }); + update_memory_buffer_finished(output, output_area, inputs, exec_system, current_pass); + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h new file mode 100644 index 00000000000..e86b1d303f9 --- /dev/null +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h @@ -0,0 +1,73 @@ +/* + * 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_NodeOperation.h" + +namespace blender::compositor { + +class MultiThreadedOperation : public NodeOperation { + protected: + /** + * Number of execution passes. + */ + int m_num_passes; + + protected: + MultiThreadedOperation(); + + /** + * Called before an update memory buffer pass is executed. Single-threaded calls. + */ + virtual void update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_rect), + blender::Span UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system), + int UNUSED(current_pass)) + { + } + + /** + * Executes operation updating output memory buffer on output_rect area. Multi-threaded calls. + */ + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &output_rect, + blender::Span inputs, + ExecutionSystem &exec_system, + int current_pass) = 0; + + /** + * Called after an update memory buffer pass is executed. Single-threaded calls. + */ + virtual void update_memory_buffer_finished(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_rect), + blender::Span UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system), + int UNUSED(current_pass)) + { + } + + private: + void update_memory_buffer(MemoryBuffer *output, + const rcti &output_rect, + blender::Span inputs, + ExecutionSystem &exec_system) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index be3ea59efa5..83de8a751c4 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -17,8 +17,10 @@ */ #include +#include #include +#include "COM_BufferOperation.h" #include "COM_ExecutionSystem.h" #include "COM_ReadBufferOperation.h" #include "COM_defines.h" @@ -175,6 +177,177 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input, return !first; } +/* -------------------------------------------------------------------- */ +/** \name Full Frame Methods + * \{ */ + +/** + * \brief Get input operation area being read by this operation on rendering given output area. + * + * Implementation don't need to ensure r_input_area is within input operation bounds. The + * caller must clamp it. + * TODO: See if it's possible to use parameter overloading (input_id for example). + * + * \param input_op_idx: Input operation index for which we want to calculate the area being read. + * \param output_area: Area being rendered by this operation. + * \param r_input_area: Returned input operation area that needs to be read in order to render + * given output area. + */ +void NodeOperation::get_area_of_interest(const int input_op_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (get_flags().is_fullframe_operation) { + r_input_area = output_area; + } + else { + /* Non full-frame operations never implement this method. To ensure correctness assume + * whole area is used. */ + NodeOperation *input_op = getInputOperation(input_op_idx); + BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight()); + } +} + +void NodeOperation::get_area_of_interest(NodeOperation *input_op, + const rcti &output_area, + rcti &r_input_area) +{ + for (int i = 0; i < getNumberOfInputSockets(); i++) { + if (input_op == getInputOperation(i)) { + get_area_of_interest(i, output_area, r_input_area); + return; + } + } + BLI_assert(!"input_op is not an input operation."); +} + +/** + * Executes operation image manipulation algorithm rendering given areas. + * \param output_buf: Buffer to write result to. + * \param areas: Areas within this operation bounds to render. + * \param inputs_bufs: Inputs operations buffers. + * \param exec_system: Execution system. + */ +void NodeOperation::render(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system) +{ + if (get_flags().is_fullframe_operation) { + render_full_frame(output_buf, areas, inputs_bufs, exec_system); + } + else { + render_full_frame_fallback(output_buf, areas, inputs_bufs, exec_system); + } +} + +/** + * Renders given areas using operations full frame implementation. + */ +void NodeOperation::render_full_frame(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system) +{ + initExecution(); + for (const rcti &area : areas) { + update_memory_buffer(output_buf, area, inputs_bufs, exec_system); + } + deinitExecution(); +} + +/** + * Renders given areas using operations tiled implementation. + */ +void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system) +{ + Vector orig_input_links = replace_inputs_with_buffers(inputs_bufs); + + initExecution(); + const bool is_output_operation = getNumberOfOutputSockets() == 0; + if (!is_output_operation && output_buf->is_a_single_elem()) { + float *output_elem = output_buf->get_elem(0, 0); + readSampled(output_elem, 0, 0, PixelSampler::Nearest); + } + else { + for (const rcti &rect : areas) { + exec_system.execute_work(rect, [=](const rcti &split_rect) { + rcti tile_rect = split_rect; + if (is_output_operation) { + executeRegion(&tile_rect, 0); + } + else { + render_tile(output_buf, &tile_rect); + } + }); + } + } + deinitExecution(); + + remove_buffers_and_restore_original_inputs(orig_input_links); +} + +void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) +{ + const bool is_complex = get_flags().complex; + void *tile_data = is_complex ? initializeTileData(tile_rect) : nullptr; + const int elem_stride = output_buf->elem_stride; + for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) { + float *output_elem = output_buf->get_elem(tile_rect->xmin, y); + if (is_complex) { + for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { + read(output_elem, x, y, tile_data); + output_elem += elem_stride; + } + } + else { + for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { + readSampled(output_elem, x, y, PixelSampler::Nearest); + output_elem += elem_stride; + } + } + } + if (tile_data) { + deinitializeTileData(tile_rect, tile_data); + } +} + +/** + * \return Replaced inputs links. + */ +Vector NodeOperation::replace_inputs_with_buffers( + Span inputs_bufs) +{ + BLI_assert(inputs_bufs.size() == getNumberOfInputSockets()); + Vector orig_links(inputs_bufs.size()); + for (int i = 0; i < inputs_bufs.size(); i++) { + NodeOperationInput *input_socket = getInputSocket(i); + BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType()); + orig_links[i] = input_socket->getLink(); + input_socket->setLink(buffer_op->getOutputSocket()); + } + return orig_links; +} + +void NodeOperation::remove_buffers_and_restore_original_inputs( + Span original_inputs_links) +{ + BLI_assert(original_inputs_links.size() == getNumberOfInputSockets()); + for (int i = 0; i < original_inputs_links.size(); i++) { + NodeOperation *buffer_op = get_input_operation(i); + BLI_assert(buffer_op != nullptr); + BLI_assert(typeid(*buffer_op) == typeid(BufferOperation)); + NodeOperationInput *input_socket = getInputSocket(i); + input_socket->setLink(original_inputs_links[i]); + delete buffer_op; + } +} + +/** \} */ + /***************** **** OpInput **** *****************/ @@ -267,6 +440,9 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat if (!node_operation_flags.use_datatype_conversion) { os << "no_conversion,"; } + if (node_operation_flags.is_fullframe_operation) { + os << "full_frame,"; + } return os; } diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index baf3a0878b9..01068c7f812 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -39,6 +39,7 @@ namespace blender::compositor { class OpenCLDevice; class ReadBufferOperation; class WriteBufferOperation; +class ExecutionSystem; class NodeOperation; typedef NodeOperation SocketReader; @@ -190,6 +191,10 @@ struct NodeOperationFlags { */ bool open_cl : 1; + /** + * TODO: Remove this flag and SingleThreadedOperation if tiled implemention is removed. + * Full-frame implemention doesn't need it. + */ bool single_threaded : 1; /** @@ -232,6 +237,11 @@ struct NodeOperationFlags { */ bool use_datatype_conversion : 1; + /** + * Has this operation fullframe implementation. + */ + bool is_fullframe_operation : 1; + NodeOperationFlags() { complex = false; @@ -247,6 +257,7 @@ struct NodeOperationFlags { is_viewer_operation = false; is_preview_operation = false; use_datatype_conversion = true; + is_fullframe_operation = false; } }; @@ -341,6 +352,13 @@ class NodeOperation { NodeOperationOutput *getOutputSocket(unsigned int index = 0); NodeOperationInput *getInputSocket(unsigned int index); + NodeOperation *get_input_operation(int index) + { + /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing + * this method. */ + return getInputOperation(index); + } + /** * \brief determine the resolution of this node * \note this method will not set the resolution, this is the responsibility of the caller @@ -537,6 +555,33 @@ class NodeOperation { return std::unique_ptr(); } + /* -------------------------------------------------------------------- */ + /** \name Full Frame Methods + * \{ */ + + void render(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system); + + /** + * Executes operation updating output memory buffer. Single-threaded calls. + */ + virtual void update_memory_buffer(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(output_area), + Span UNUSED(inputs), + ExecutionSystem &UNUSED(exec_system)) + { + } + + /** + * Get input operation area being read by this operation on rendering given output area. + */ + virtual void get_area_of_interest(int input_op_idx, const rcti &output_area, rcti &r_input_area); + void get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area); + + /** \} */ + protected: NodeOperation(); @@ -616,6 +661,27 @@ class NodeOperation { { } + private: + /* -------------------------------------------------------------------- */ + /** \name Full Frame Methods + * \{ */ + + void render_full_frame(MemoryBuffer *output_buf, + Span areas, + Span inputs_bufs, + ExecutionSystem &exec_system); + + void render_full_frame_fallback(MemoryBuffer *output_buf, + Span areas, + Span inputs, + ExecutionSystem &exec_system); + void render_tile(MemoryBuffer *output_buf, rcti *tile_rect); + Vector replace_inputs_with_buffers(Span inputs_bufs); + void remove_buffers_and_restore_original_inputs( + Span original_inputs_links); + + /** \} */ + /* allow the DebugInfo class to look at internals */ friend class DebugInfo; diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 82eb969b752..c81a5a2bd98 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -99,8 +99,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) determineResolutions(); - /* surround complex ops with read/write buffer */ - add_complex_operation_buffers(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { + /* surround complex ops with read/write buffer */ + add_complex_operation_buffers(); + } /* links not available from here on */ /* XXX make m_links a local variable to avoid confusion! */ @@ -111,8 +113,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) /* ensure topological (link-based) order of nodes */ /*sort_operations();*/ /* not needed yet */ - /* create execution groups */ - group_operations(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { + /* create execution groups */ + group_operations(); + } /* transfer resulting operations to the system */ system->set_operations(m_operations, m_groups); diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc new file mode 100644 index 00000000000..021e948a727 --- /dev/null +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.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. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_SharedOperationBuffers.h" +#include "BLI_rect.h" +#include "COM_NodeOperation.h" + +namespace blender::compositor { + +SharedOperationBuffers::SharedOperationBuffers() : buffers_() +{ +} +SharedOperationBuffers::BufferData::BufferData() + : buffer(nullptr), render_areas(), registered_reads(0), received_reads(0) +{ +} + +SharedOperationBuffers::BufferData &SharedOperationBuffers::get_buffer_data(NodeOperation *op) +{ + return buffers_.lookup_or_add_cb(op, []() { return BufferData(); }); +} + +/** + * Whether given operation area to render is already registered. + * TODO: Possibly refactor to "request_area". Current implementation is incomplete: partial + * overlapping, etc. Leading to more rendering than necessary. + */ +bool SharedOperationBuffers::is_area_registered(NodeOperation *op, const rcti &area_to_render) +{ + BufferData &buf_data = get_buffer_data(op); + for (rcti ®_rect : buf_data.render_areas) { + if (BLI_rcti_inside_rcti(®_rect, &area_to_render)) { + return true; + } + } + return false; +} + +/** + * Registers an operation area to render. + */ +void SharedOperationBuffers::register_area(NodeOperation *op, const rcti &area_to_render) +{ + get_buffer_data(op).render_areas.append(area_to_render); +} + +/** + * Whether given operation has any registered reads (other operation registered it depends on given + * operation). + */ +bool SharedOperationBuffers::has_registered_reads(NodeOperation *op) +{ + return get_buffer_data(op).registered_reads > 0; +} + +/** + * Registers an operation read (other operation depends on given operation). + */ +void SharedOperationBuffers::register_read(NodeOperation *read_op) +{ + get_buffer_data(read_op).registered_reads++; +} + +/** + * Get registered areas given operation needs to render. + */ +blender::Span SharedOperationBuffers::get_areas_to_render(NodeOperation *op) +{ + return get_buffer_data(op).render_areas.as_span(); +} + +/** + * Whether this operation buffer has already been rendered. + */ +bool SharedOperationBuffers::is_operation_rendered(NodeOperation *op) +{ + return get_buffer_data(op).buffer != nullptr; +} + +/** + * Stores given operation rendered buffer. + */ +void SharedOperationBuffers::set_rendered_buffer(NodeOperation *op, + std::unique_ptr buffer) +{ + BufferData &buf_data = get_buffer_data(op); + BLI_assert(buf_data.received_reads == 0); + BLI_assert(buf_data.buffer == nullptr); + buf_data.buffer = std::move(buffer); +} + +/** + * Get given operation rendered buffer. + */ +MemoryBuffer *SharedOperationBuffers::get_rendered_buffer(NodeOperation *op) +{ + BLI_assert(is_operation_rendered(op)); + return get_buffer_data(op).buffer.get(); +} + +/** + * Reports an operation has finished reading given operation. If all given operation dependencies + * have finished its buffer will be disposed. + */ +void SharedOperationBuffers::read_finished(NodeOperation *read_op) +{ + BufferData &buf_data = get_buffer_data(read_op); + buf_data.received_reads++; + BLI_assert(buf_data.received_reads > 0 && buf_data.received_reads <= buf_data.registered_reads); + if (buf_data.received_reads == buf_data.registered_reads) { + /* Dispose buffer. */ + buf_data.buffer = nullptr; + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h new file mode 100644 index 00000000000..9e90a06a0d3 --- /dev/null +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h @@ -0,0 +1,71 @@ +/* + * 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 "BLI_map.hh" +#include "BLI_span.hh" +#include "BLI_vector.hh" +#include "COM_MemoryBuffer.h" +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif +#include + +namespace blender::compositor { + +/** + * Stores and shares operations rendered buffers including render data. Buffers are + * disposed once all dependent operations have finished reading them. + */ +class SharedOperationBuffers { + private: + typedef struct BufferData { + public: + BufferData(); + std::unique_ptr buffer; + blender::Vector render_areas; + int registered_reads; + int received_reads; + } BufferData; + blender::Map buffers_; + + public: + SharedOperationBuffers(); + bool is_area_registered(NodeOperation *op, const rcti &area_to_render); + void register_area(NodeOperation *op, const rcti &area_to_render); + + bool has_registered_reads(NodeOperation *op); + void register_read(NodeOperation *read_op); + + blender::Span get_areas_to_render(NodeOperation *op); + bool is_operation_rendered(NodeOperation *op); + void set_rendered_buffer(NodeOperation *op, std::unique_ptr buffer); + MemoryBuffer *get_rendered_buffer(NodeOperation *op); + + void read_finished(NodeOperation *read_op); + + private: + BufferData &get_buffer_data(NodeOperation *op); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:SharedOperationBuffers") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc new file mode 100644 index 00000000000..d025ce53330 --- /dev/null +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc @@ -0,0 +1,158 @@ +/* + * 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_TiledExecutionModel.h" +#include "COM_Debug.h" +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" +#include "COM_WorkScheduler.h" + +#include "BLT_translation.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +TiledExecutionModel::TiledExecutionModel(CompositorContext &context, + Span operations, + Span groups) + : ExecutionModel(context, operations), groups_(groups) +{ + const bNodeTree *node_tree = context.getbNodeTree(); + node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Determining resolution")); + + unsigned int resolution[2]; + for (ExecutionGroup *group : groups_) { + resolution[0] = 0; + resolution[1] = 0; + group->determineResolution(resolution); + + if (border_.use_render_border) { + const rctf *render_border = border_.viewer_border; + group->setRenderBorder( + render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax); + } + + if (border_.use_viewer_border) { + const rctf *viewer_border = border_.viewer_border; + group->setViewerBorder( + viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); + } + } +} + +static void update_read_buffer_offset(Span operations) +{ + unsigned int order = 0; + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_read_buffer_operation) { + ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; + readOperation->setOffset(order); + order++; + } + } +} + +static void init_write_operations_for_execution(Span operations, + const bNodeTree *bTree) +{ + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_write_buffer_operation) { + operation->setbNodeTree(bTree); + operation->initExecution(); + } + } +} + +static void link_write_buffers(Span operations) +{ + for (NodeOperation *operation : operations) { + if (operation->get_flags().is_read_buffer_operation) { + ReadBufferOperation *readOperation = static_cast(operation); + readOperation->updateMemoryBuffer(); + } + } +} + +static void init_non_write_operations_for_execution(Span operations, + const bNodeTree *bTree) +{ + for (NodeOperation *operation : operations) { + if (!operation->get_flags().is_write_buffer_operation) { + operation->setbNodeTree(bTree); + operation->initExecution(); + } + } +} + +static void init_execution_groups_for_execution(Span groups, + const int chunk_size) +{ + for (ExecutionGroup *execution_group : groups) { + execution_group->setChunksize(chunk_size); + execution_group->initExecution(); + } +} + +void TiledExecutionModel::execute(ExecutionSystem &exec_system) +{ + const bNodeTree *editingtree = this->context_.getbNodeTree(); + + editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); + + update_read_buffer_offset(operations_); + + init_write_operations_for_execution(operations_, context_.getbNodeTree()); + link_write_buffers(operations_); + init_non_write_operations_for_execution(operations_, context_.getbNodeTree()); + init_execution_groups_for_execution(groups_, context_.getChunksize()); + + WorkScheduler::start(context_); + execute_groups(eCompositorPriority::High, exec_system); + if (!context_.isFastCalculation()) { + execute_groups(eCompositorPriority::Medium, exec_system); + execute_groups(eCompositorPriority::Low, exec_system); + } + WorkScheduler::finish(); + WorkScheduler::stop(); + + editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); + + for (NodeOperation *operation : operations_) { + operation->deinitExecution(); + } + + for (ExecutionGroup *execution_group : groups_) { + execution_group->deinitExecution(); + } +} + +void TiledExecutionModel::execute_groups(eCompositorPriority priority, + ExecutionSystem &exec_system) +{ + for (ExecutionGroup *execution_group : groups_) { + if (execution_group->get_flags().is_output && + execution_group->getRenderPriority() == priority) { + execution_group->execute(&exec_system); + } + } +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.h b/source/blender/compositor/intern/COM_TiledExecutionModel.h new file mode 100644 index 00000000000..05a795b9f07 --- /dev/null +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.h @@ -0,0 +1,54 @@ +/* + * 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_ExecutionModel.h" + +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif + +namespace blender::compositor { + +class ExecutionGroup; + +/** + * Operations are executed from outputs to inputs grouped in execution groups and rendered in + * tiles. + */ +class TiledExecutionModel : public ExecutionModel { + private: + Span groups_; + + public: + TiledExecutionModel(CompositorContext &context, + Span operations, + Span groups); + + void execute(ExecutionSystem &exec_system) override; + + private: + void execute_groups(eCompositorPriority priority, ExecutionSystem &exec_system); + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("COM:TiledExecutionModel") +#endif +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h index 28aa746fdc4..4d503022120 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.h +++ b/source/blender/compositor/intern/COM_WorkPackage.h @@ -22,6 +22,7 @@ #include "BLI_rect.h" +#include #include namespace blender::compositor { @@ -33,6 +34,8 @@ class ExecutionGroup; * \see WorkScheduler */ struct WorkPackage { + eWorkPackageType type; + eWorkPackageState state = eWorkPackageState::NotScheduled; /** @@ -50,6 +53,16 @@ struct WorkPackage { */ rcti rect; + /** + * Custom function to execute when work package type is CustomFunction. + */ + std::function execute_fn; + + /** + * Called when work execution is finished. + */ + std::function executed_fn; + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkPackage") #endif diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index d578ac24a4a..157ded943d6 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -98,6 +98,8 @@ static struct { bool active = false; bool initialized = false; } opencl; + + int num_cpu_threads; } g_work_scheduler; /* -------------------------------------------------------------------- */ @@ -143,7 +145,8 @@ static void opencl_start(CompositorContext &context) static bool opencl_schedule(WorkPackage *package) { - if (package->execution_group->get_flags().open_cl && g_work_scheduler.opencl.active) { + if (package->type == eWorkPackageType::Tile && package->execution_group->get_flags().open_cl && + g_work_scheduler.opencl.active) { BLI_thread_queue_push(g_work_scheduler.opencl.queue, package); return true; } @@ -532,11 +535,12 @@ void WorkScheduler::initialize(bool use_opencl, int num_cpu_threads) opencl_initialize(use_opencl); } + g_work_scheduler.num_cpu_threads = num_cpu_threads; switch (COM_threading_model()) { case ThreadingModel::SingleThreaded: + g_work_scheduler.num_cpu_threads = 1; /* Nothing to do. */ break; - case ThreadingModel::Queue: threading_model_queue_initialize(num_cpu_threads); break; @@ -568,6 +572,11 @@ void WorkScheduler::deinitialize() } } +int WorkScheduler::get_num_cpu_threads() +{ + return g_work_scheduler.num_cpu_threads; +} + int WorkScheduler::current_thread_id() { if (COM_threading_model() == ThreadingModel::SingleThreaded) { diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h index 85b1d7e2ebf..be88859be7c 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.h +++ b/source/blender/compositor/intern/COM_WorkScheduler.h @@ -87,6 +87,8 @@ struct WorkScheduler { */ static bool has_gpu_devices(); + static int get_num_cpu_threads(); + static int current_thread_id(); #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 922393f006a..1b2ce341a66 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -56,7 +56,10 @@ void TranslateNode::convertToOperations(NodeConverter &converter, converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); - if (data->wrap_axis) { + /* FullFrame does not support using WriteBufferOperation. + * TODO: Implement TranslateOperation with wrap support in FullFrame. + */ + if (data->wrap_axis && context.get_execution_model() != eExecutionModel::FullFrame) { WriteBufferOperation *writeOperation = new WriteBufferOperation(DataType::Color); WrapOperation *wrapOperation = new WrapOperation(DataType::Color); wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy()); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 58c94b6f369..0fa1d1a74ac 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -461,7 +461,6 @@ typedef struct bNodeTree { short is_updating; /** Generic temporary flag for recursion check (DFS/BFS). */ short done; - char _pad2[4]; /** Specific node type this tree is used for. */ int nodetype DNA_DEPRECATED; @@ -472,6 +471,8 @@ typedef struct bNodeTree { short render_quality; /** Tile size for compositor engine. */ int chunksize; + /** Execution mode to use for compositor engine. */ + int execution_mode; rctf viewer_border; @@ -545,6 +546,12 @@ typedef enum eNodeTreeUpdate { NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT), } eNodeTreeUpdate; +/* tree->execution_mode */ +typedef enum eNodeTreeExecutionMode { + NTREE_EXECUTION_MODE_TILED = 0, + NTREE_EXECUTION_MODE_FULL_FRAME = 1, +} eNodeTreeExecutionMode; + /* socket value structs for input buttons * DEPRECATED now using ID properties */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 1fed8e14bd6..61d2c04d98b 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -642,11 +642,12 @@ typedef struct UserDef_Experimental { * when the release cycle is not alpha. */ char use_new_hair_type; char use_new_point_cloud_type; + char use_full_frame_compositor; char use_sculpt_vertex_colors; char use_sculpt_tools_tilt; char use_asset_browser; char use_override_templates; - char _pad[6]; + char _pad[5]; /** `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 0ca275eb58b..f5cbb694554 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -127,6 +127,20 @@ static const EnumPropertyItem node_chunksize_items[] = { }; #endif +static const EnumPropertyItem rna_enum_execution_mode_items[] = { + {NTREE_EXECUTION_MODE_TILED, + "TILED", + 0, + "Tiled", + "Compositing is tiled, having as priority to display first tiles as fast as possible"}, + {NTREE_EXECUTION_MODE_FULL_FRAME, + "FULL_FRAME", + 0, + "Full Frame", + "Composites full image result as fast as possible"}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_mapping_type_items[] = { {NODE_MAPPING_TYPE_POINT, "POINT", 0, "Point", "Transform a point"}, {NODE_MAPPING_TYPE_TEXTURE, @@ -11671,6 +11685,12 @@ static void rna_def_composite_nodetree(BlenderRNA *brna) RNA_def_struct_sdna(srna, "bNodeTree"); RNA_def_struct_ui_icon(srna, ICON_RENDERLAYERS); + prop = RNA_def_property(srna, "execution_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "execution_mode"); + RNA_def_property_enum_items(prop, rna_enum_execution_mode_items); + RNA_def_property_ui_text(prop, "Execution Mode", "Set how compositing is executed"); + RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update"); + prop = RNA_def_property(srna, "render_quality", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "render_quality"); RNA_def_property_enum_items(prop, node_quality_items); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index bacd3943141..6005ec96255 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6276,6 +6276,14 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "New Point Cloud Type", "Enable the new point cloud type in the ui"); + prop = RNA_def_property(srna, "use_full_frame_compositor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_full_frame_compositor", 1); + RNA_def_property_ui_text(prop, + "Full Frame Compositor", + "Enable compositor full frame execution mode option (no tiling, " + "reduces execution time and memory usage)"); + RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1); RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui"); -- cgit v1.2.3 From 27910ccce6a0a6a79b09144631309331cb557a25 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 1 Jun 2021 12:00:16 +0200 Subject: Cleanup: clang-tidy * `readability-redundant-member-init` * `readability-inconsistent-declaration-parameter-name` * Remove constructor that can be defaulted. --- source/blender/compositor/intern/COM_BufferOperation.cc | 2 +- source/blender/compositor/intern/COM_FullFrameExecutionModel.cc | 1 - source/blender/compositor/intern/COM_MultiThreadedOperation.h | 2 +- source/blender/compositor/intern/COM_SharedOperationBuffers.cc | 5 +---- source/blender/compositor/intern/COM_SharedOperationBuffers.h | 1 - 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc index c1e64142443..8464d01801f 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.cc +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -20,7 +20,7 @@ namespace blender::compositor { -BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) : NodeOperation() +BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) { buffer_ = buffer; /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index 1099aadd89d..396aa2fcf6f 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -36,7 +36,6 @@ FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, : ExecutionModel(context, operations), active_buffers_(shared_buffers), num_operations_finished_(0), - priorities_(), work_mutex_(), work_finished_cond_() { diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h index e86b1d303f9..97c5fba4ead 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedOperation.h +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h @@ -65,7 +65,7 @@ class MultiThreadedOperation : public NodeOperation { private: void update_memory_buffer(MemoryBuffer *output, - const rcti &output_rect, + const rcti &output_area, blender::Span inputs, ExecutionSystem &exec_system) override; }; diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc index 021e948a727..4ce674a1c25 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc @@ -22,11 +22,8 @@ namespace blender::compositor { -SharedOperationBuffers::SharedOperationBuffers() : buffers_() -{ -} SharedOperationBuffers::BufferData::BufferData() - : buffer(nullptr), render_areas(), registered_reads(0), received_reads(0) + : buffer(nullptr), registered_reads(0), received_reads(0) { } diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h index 9e90a06a0d3..480a799d89f 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.h +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h @@ -46,7 +46,6 @@ class SharedOperationBuffers { blender::Map buffers_; public: - SharedOperationBuffers(); bool is_area_registered(NodeOperation *op, const rcti &area_to_render); void register_area(NodeOperation *op, const rcti &area_to_render); -- cgit v1.2.3 From db15c9d1bd9dd85ef1db0df24f4ad56c0b2012fe Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 11:39:28 +0200 Subject: ID Management: Allow unique name check for linked IDs too. This is mandatory for liboverride resync, since this feature may imply we have to create linked overrides in libraries, and there may be several copies of those. This is also a first step to a more general support of IDmanagement-editing library data. Note that this commit should have absolutely no effect on current code, as the only function allowed to check unique names for linked IDs currently is `BKE_libblock_management_main_add`, which is unused. This commit also adds some basic testing for `BKE_id_new_name_validate`. --- source/blender/blenkernel/BKE_lib_id.h | 6 ++- source/blender/blenkernel/intern/lib_id.c | 27 +++++----- source/blender/blenkernel/intern/lib_id_test.cc | 60 +++++++++++++++++++++++ source/blender/blenloader/intern/versioning_250.c | 2 +- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 7ac45ac4883..e16507bf3cc 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -258,8 +258,10 @@ void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint); void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id); -bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name) - ATTR_NONNULL(1, 2); +bool BKE_id_new_name_validate(struct ListBase *lb, + struct ID *id, + const char *name, + const bool do_linked_data) ATTR_NONNULL(1, 2); void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id); /* Affect whole Main database. */ diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index f93bf494ee9..8a6fb5b3e9e 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -164,7 +164,7 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id) id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN); id->flag &= ~LIB_INDIRECT_WEAK_LINK; if (id_in_mainlist) { - if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL)) { + if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL, false)) { bmain->is_memfile_undo_written = false; } } @@ -833,7 +833,9 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv) ListBase *lb = which_libbase(bmain, GS(id->name)); BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, NULL); + /* We need to allow adding extra datablocks into libraries too, e.g. to support generating new + * overrides for recursive resync. */ + BKE_id_new_name_validate(lb, id, NULL, true); /* alphabetic insertion: is in new_id */ id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT); bmain->is_memfile_undo_written = false; @@ -989,7 +991,7 @@ void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) } for (i = 0; i < lb_len; i++) { if (!BLI_gset_add(gset, id_array[i]->name + 2)) { - BKE_id_new_name_validate(lb, id_array[i], NULL); + BKE_id_new_name_validate(lb, id_array[i], NULL, false); } } BLI_gset_free(gset, NULL); @@ -1092,7 +1094,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); bmain->is_memfile_undo_written = false; /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); @@ -1557,7 +1559,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * and that current one is not. */ bool is_valid = false; for (id_test = lb->first; id_test; id_test = id_test->next) { - if (id != id_test && !ID_IS_LINKED(id_test)) { + if (id != id_test && id_test->lib == id->lib) { if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) { /* We expect final_name to not be already used, so this is a failure. */ is_valid = false; @@ -1613,7 +1615,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ for (id_test = lb->first; id_test; id_test = id_test->next) { char base_name_test[MAX_ID_NAME - 2]; int number_test; - if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) && + if ((id != id_test) && (id_test->lib == id->lib) && (name[0] == id_test->name[2]) && (ELEM(id_test->name[base_name_len + 2], '.', '\0')) && STREQLEN(name, id_test->name + 2, base_name_len) && (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == @@ -1702,15 +1704,18 @@ 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 + * (otherwise, just ensure that it is properly sorted). + * * \return true if a new name had to be created. */ -bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) +bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data) { bool result = false; char name[MAX_ID_NAME - 2]; - /* If library, don't rename, but do ensure proper sorting. */ - if (ID_IS_LINKED(id)) { + /* If library, don't rename (unless explicitely required), but do ensure proper sorting. */ + if (!do_linked_data && ID_IS_LINKED(id)) { id_sort_by_name(lb, id, NULL); return result; @@ -2195,7 +2200,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); if (idtest != NULL) { /* BKE_id_new_name_validate also takes care of sorting. */ - BKE_id_new_name_validate(lb, idtest, NULL); + BKE_id_new_name_validate(lb, idtest, NULL, false); bmain->is_memfile_undo_written = false; } } @@ -2206,7 +2211,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) void BKE_libblock_rename(Main *bmain, ID *id, const char *name) { ListBase *lb = which_libbase(bmain, GS(id->name)); - if (BKE_id_new_name_validate(lb, id, name)) { + if (BKE_id_new_name_validate(lb, id, name, false)) { bmain->is_memfile_undo_written = false; } } diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index fbe4a15da1c..8e21ae88aa6 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -20,6 +20,7 @@ #include "MEM_guardedalloc.h" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -110,4 +111,63 @@ TEST(lib_id_main_sort, linked_ids_1) test_lib_id_main_sort_free(&ctx); } +TEST(lib_id_main_unique_name, local_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + test_lib_id_main_sort_check_order({id_a, id_b, id_c}); + + BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_c, NULL, false); + EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_a); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_a, id_c, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + +TEST(lib_id_main_unique_name, linked_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + Library *lib_a = static_cast(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); + Library *lib_b = static_cast(BKE_id_new(ctx.bmain, ID_LI, "LI_B")); + ID *id_c = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + + id_a->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); + id_b->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + id_b->lib = lib_b; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + } // namespace blender::bke::tests diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 6338dc95aba..990fc1d65d7 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -446,7 +446,7 @@ static void versions_gpencil_add_main(ListBase *lb, ID *id, const char *name) id->flag = LIB_FAKEUSER; *((short *)id->name) = ID_GD; - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); /* alphabetic insertion: is in BKE_id_new_name_validate */ if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) { -- cgit v1.2.3 From 84e16c499243f6bdeb29f684408f4d206b919ee8 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 11:46:43 +0200 Subject: Fix `BLI_libblock_ensure_unique_name` not ignoring linked IDs. This function would considere that there was a name conflict even in case existing ID would be a linked one. This is only a (symbolic) perforance improvement and logical fix, since `BKE_id_new_name_validate` would not do that mistake anyway. --- source/blender/blenkernel/intern/lib_id.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 8a6fb5b3e9e..2dd0dc2c097 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -2198,7 +2198,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) /* search for id */ idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); - if (idtest != NULL) { + if (idtest != NULL && !ID_IS_LINKED(idtest)) { /* BKE_id_new_name_validate also takes care of sorting. */ BKE_id_new_name_validate(lb, idtest, NULL, false); bmain->is_memfile_undo_written = false; -- cgit v1.2.3 From f25316e97a81650b85c5e2a91c90b7dd558240e5 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 11:49:15 +0200 Subject: IDManagement: `BKE_libblock_rename`: assert we get local ID. For now at least, linked data should never be renamed that way. --- source/blender/blenkernel/intern/lib_id.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 2dd0dc2c097..490abe05169 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -2210,6 +2210,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) */ void BKE_libblock_rename(Main *bmain, ID *id, const char *name) { + BLI_assert(!ID_IS_LINKED(id)); ListBase *lb = which_libbase(bmain, GS(id->name)); if (BKE_id_new_name_validate(lb, id, name, false)) { bmain->is_memfile_undo_written = false; -- cgit v1.2.3 From bf8659930fe8719d0918fe7da8ed8718b6ce2d94 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 11:56:41 +0200 Subject: Fix libOverride resync issues in some corner-cases. This commit fixes two different issues: * In some cases, when an object was added to a sub-collection and used into a different subcollection, and the root common collection would not need to be resynced, it would end up creating multiple overrides of the new object. This was affecting both normal and recursive resync. * In recurisve resync case, the barrier code to define what is part or not of a override group/hierarchy was wrong. Note that the current solution for the first issue is sub-optimal (it goes back to the root of the override hierarchy and resync the whole thing), a better solution is TODO for now. --- source/blender/blenkernel/intern/lib_override.c | 106 ++++++++++++++++++++---- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 83bd2b6abb7..5af37aeb13e 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -428,7 +428,8 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, /* If other ID is a linked one, but not from the same library as our reference, then we * consider we should also remap it, as part of recursive resync. */ - if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib) { + if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib && + other_id != local_id) { BKE_libblock_relink_ex(bmain, other_id, reference_id, @@ -468,6 +469,8 @@ typedef struct LibOverrideGroupTagData { ID *id_root; uint tag; uint missing_tag; + /* Whether we are looping on override data, or their references (linked) one. */ + bool is_override; } LibOverrideGroupTagData; /* Tag all IDs in dependency relationships within an override hierarchy/group. @@ -480,6 +483,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa { Main *bmain = data->bmain; ID *id = data->id_root; + const bool is_override = data->is_override; MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); BLI_assert(entry != NULL); @@ -501,16 +505,16 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa } /* We only consider IDs from the same library. */ ID *to_id = *to_id_entry->id_pointer.to; - if (!ID_IS_LINKED(to_id) && !ID_IS_OVERRIDE_LIBRARY(to_id)) { - /* Pure local data is a barrier of dependency in override cases. */ + if (to_id == NULL || to_id->lib != id->lib || + (is_override && !ID_IS_OVERRIDE_LIBRARY(to_id))) { + /* IDs from different libraries, or non-override IDs in case we are processing overrides, are + * both barriers of dependency. */ continue; } - if (to_id != NULL && to_id->lib == id->lib) { - LibOverrideGroupTagData sub_data = *data; - sub_data.id_root = to_id; - if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { - id->tag |= data->tag; - } + LibOverrideGroupTagData sub_data = *data; + sub_data.id_root = to_id; + if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { + id->tag |= data->tag; } } @@ -522,6 +526,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_LINKED(id_owner)); + BLI_assert(!data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -587,6 +592,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_root = data->id_root; + BLI_assert(!data->is_override); if ((id_root->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -618,6 +624,7 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner)); + BLI_assert(data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -674,6 +681,7 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data) { ID *id_root = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); + BLI_assert(data->is_override); if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -689,8 +697,11 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data) static bool lib_override_library_create_do(Main *bmain, ID *id_root) { BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -941,12 +952,16 @@ bool BKE_lib_override_library_resync(Main *bmain, ID *id_root_reference = id_root->override_library->reference; BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; lib_override_local_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); data.id_root = id_root_reference; + data.is_override = false; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -1284,6 +1299,58 @@ bool BKE_lib_override_library_resync(Main *bmain, return success; } +/* Ensures parent collection (or objects) in the same override group are also tagged for resync. + * + * This is needed since otherwise, some (new) ID added in one sub-collection might be used in + * another unrelated sub-collection, if 'root' collection is not resynced separated resync of those + * sub-collections would be unaware that this is the same ID, and would re-generate several + * overrides for it. + * + * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to + * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and + * resync the whole hierarchy. */ +static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level) +{ + (*level)++; + ID *return_id = id; + + switch (GS(id->name)) { + case ID_GR: { + /* Find the highest valid collection in the parenting hierarchy. + * Note that in practice, in any decent common case there is only one well defined root + * collection anyway. */ + int max_level = *level; + Collection *collection = (Collection *)id; + LISTBASE_FOREACH (CollectionParent *, collection_parent_iter, &collection->parents) { + Collection *collection_parent = collection_parent_iter->collection; + if (ID_IS_OVERRIDE_LIBRARY_REAL(collection_parent) && + collection_parent->id.lib == id->lib) { + int tmp_level = *level; + ID *tmp_id = lib_override_library_main_resync_find_root_recurse(&collection_parent->id, + &tmp_level); + if (tmp_level > max_level) { + max_level = tmp_level; + return_id = tmp_id; + } + } + } + break; + } + case ID_OB: { + Object *object = (Object *)id; + if (object->parent != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(object->parent) && + object->parent->id.lib == id->lib) { + return_id = lib_override_library_main_resync_find_root_recurse(&object->parent->id, level); + } + break; + } + default: + break; + } + + return return_id; +} + /* Ensure resync of all overrides at one level of indirect usage. * * We need to handle each level independently, since an override at level n may be affected by @@ -1319,7 +1386,8 @@ static void lib_override_library_main_resync_on_library_indirect_level( LibOverrideGroupTagData data = {.bmain = bmain, .id_root = id->override_library->reference, .tag = LIB_TAG_DOIT, - .missing_tag = LIB_TAG_MISSING}; + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); lib_override_hierarchy_dependencies_recursive_tag(&data); @@ -1403,6 +1471,9 @@ static void lib_override_library_main_resync_on_library_indirect_level( (!ID_IS_LINKED(id) && library_indirect_level != 0)) { continue; } + + int level = 0; + id = lib_override_library_main_resync_find_root_recurse(id, &level); BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); do_continue = true; @@ -1557,8 +1628,11 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) /* Tag all library overrides in the chains of dependencies from the given root one. */ BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; lib_override_local_group_tag(&data); BKE_main_relations_free(bmain); -- cgit v1.2.3 From 4b57bbac2262df1a24099059450508e89ee00df1 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 12:29:56 +0200 Subject: Cleanup: LibOverride: rename 'local' group tagging functions to 'overrides'. Since we use them on linked data now as well, 'local' does not fit them anymore. --- source/blender/blenkernel/intern/lib_override.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 5af37aeb13e..4d5085d6ad5 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -619,7 +619,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) } } -static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_owner = data->id_root; @@ -672,12 +672,12 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data /* Recursively process the dependencies. */ LibOverrideGroupTagData sub_data = *data; sub_data.id_root = to_id; - lib_override_local_group_tag_recursive(&sub_data); + lib_override_overrides_group_tag_recursive(&sub_data); } } /* This will tag all override IDs of an override group defined by the given `id_root`. */ -static void lib_override_local_group_tag(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) { ID *id_root = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); @@ -691,7 +691,7 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data) } /* Tag all local overrides in id_root's group. */ - lib_override_local_group_tag_recursive(data); + lib_override_overrides_group_tag_recursive(data); } static bool lib_override_library_create_do(Main *bmain, ID *id_root) @@ -957,7 +957,7 @@ bool BKE_lib_override_library_resync(Main *bmain, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, .is_override = true}; - lib_override_local_group_tag(&data); + lib_override_overrides_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); data.id_root = id_root_reference; @@ -1633,7 +1633,7 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, .is_override = true}; - lib_override_local_group_tag(&data); + lib_override_overrides_group_tag(&data); BKE_main_relations_free(bmain); -- cgit v1.2.3 From 98c662672998a16a7ac00ab283357de17f3ae54c Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 12:12:13 +0200 Subject: DrawManager: Use CPP for Mesh Extraction Scheduling. More cleanups will come to make this more CPP-like. --- source/blender/draw/CMakeLists.txt | 2 +- source/blender/draw/intern/draw_cache_extract.h | 11 +- .../blender/draw/intern/draw_cache_extract_mesh.c | 987 ------------------- .../blender/draw/intern/draw_cache_extract_mesh.cc | 1006 ++++++++++++++++++++ .../draw/intern/draw_cache_extract_mesh_private.h | 8 + 5 files changed, 1025 insertions(+), 989 deletions(-) delete mode 100644 source/blender/draw/intern/draw_cache_extract_mesh.c create mode 100644 source/blender/draw/intern/draw_cache_extract_mesh.cc diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index adbe7fdf274..d6598bf79b0 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -53,7 +53,7 @@ set(SRC intern/draw_cache.c intern/draw_cache_extract_mesh_extractors.c intern/draw_cache_extract_mesh_render_data.c - intern/draw_cache_extract_mesh.c + intern/draw_cache_extract_mesh.cc intern/draw_cache_impl_curve.cc intern/draw_cache_impl_displist.c intern/draw_cache_impl_gpencil.c diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index abba3aeeb70..36756616ca7 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -68,13 +68,13 @@ typedef struct DRW_MeshCDMask { * bit-wise and atomic operations are used to compare and update the struct. * See `mesh_cd_layers_type_*` functions. */ BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") - typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, MR_ITER_POLY = 1 << 1, MR_ITER_LEDGE = 1 << 2, MR_ITER_LVERT = 1 << 3, } eMRIterType; +ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) typedef enum eMRDataType { MR_DATA_POLY_NOR = 1 << 1, @@ -83,6 +83,11 @@ typedef enum eMRDataType { /** Force loop normals calculation. */ MR_DATA_TAN_LOOP_NOR = 1 << 4, } eMRDataType; +ENUM_OPERATORS(eMRDataType, MR_DATA_TAN_LOOP_NOR) + +#ifdef __cplusplus +extern "C" { +#endif BLI_INLINE int mesh_render_mat_len_get(Mesh *me) { @@ -298,3 +303,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const Scene *scene, const ToolSettings *ts, const bool use_hide); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c deleted file mode 100644 index 0d2a4704b1b..00000000000 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ /dev/null @@ -1,987 +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) 2017 by Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup draw - * - * \brief Extraction of Mesh data into VBO to feed to GPU. - */ -#include "MEM_guardedalloc.h" - -#include "atomic_ops.h" - -#include "DNA_mesh_types.h" -#include "DNA_scene_types.h" - -#include "BLI_task.h" - -#include "BKE_editmesh.h" - -#include "GPU_capabilities.h" - -#include "draw_cache_extract.h" -#include "draw_cache_extract_mesh_private.h" -#include "draw_cache_inline.h" - -// #define DEBUG_TIME - -#ifdef DEBUG_TIME -# include "PIL_time_utildefines.h" -#endif - -#define CHUNK_SIZE 8192 - -/* ---------------------------------------------------------------------- */ -/** \name Mesh Elements Extract Struct - * \{ */ - -typedef struct MeshExtractRunData { - const MeshExtract *extractor; - void *buffer; - void *user_data; -} MeshExtractRunData; - -typedef struct MeshExtractRunDataArray { - int len; - MeshExtractRunData items[M_EXTRACT_LEN]; -} MeshExtractRunDataArray; - -static void mesh_extract_run_data_array_init(MeshExtractRunDataArray *array) -{ - array->len = 0; -} - -static void mesh_extract_run_data_array_add_ex(MeshExtractRunDataArray *array, - const MeshExtractRunData *run_data) -{ - array->items[array->len] = *run_data; - array->len++; -} - -static void mesh_extract_run_data_array_add(MeshExtractRunDataArray *array, - const MeshExtract *extractor) -{ - MeshExtractRunData run_data; - run_data.extractor = extractor; - run_data.buffer = NULL; - run_data.user_data = NULL; - mesh_extract_run_data_array_add_ex(array, &run_data); -} - -static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray *src, - MeshExtractRunDataArray *dst, - eMRIterType iter_type) -{ - for (int i = 0; i < src->len; i++) { - - const MeshExtractRunData *data = &src->items[i]; - const MeshExtract *extractor = data->extractor; - if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { - BLI_assert(extractor->iter_looptri_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { - BLI_assert(extractor->iter_poly_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { - BLI_assert(extractor->iter_ledge_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { - BLI_assert(extractor->iter_lvert_mesh); - mesh_extract_run_data_array_add_ex(dst, data); - continue; - } - } -} - -static void mesh_extract_run_data_array_filter_threading( - const MeshExtractRunDataArray *src, MeshExtractRunDataArray *dst_multi_threaded) -{ - for (int i = 0; i < src->len; i++) { - const MeshExtract *extractor = src->items[i].extractor; - if (extractor->use_threading) { - mesh_extract_run_data_array_add(dst_multi_threaded, extractor); - } - } -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract - * \{ */ - -static void extracts_flags_get(const MeshExtractRunDataArray *extractors, - eMRIterType *r_iter_type, - eMRDataType *r_data_flag) -{ - eMRIterType iter_type = 0; - eMRDataType data_flag = 0; - - for (int i = 0; i < extractors->len; i++) { - const MeshExtract *extractor = extractors->items[i].extractor; - iter_type |= mesh_extract_iter_type(extractor); - data_flag |= extractor->data_flag; - } - - if (r_iter_type) { - *r_iter_type = iter_type; - } - if (r_data_flag) { - *r_data_flag = data_flag; - } -} - -BLI_INLINE void extract_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, - MeshBufferCache *mbc) -{ - /* Multi thread. */ - for (int i = 0; i < extractors->len; i++) { - MeshExtractRunData *run_data = &extractors->items[i]; - const MeshExtract *extractor = run_data->extractor; - run_data->buffer = mesh_extract_buffer_get(extractor, mbc); - run_data->user_data = extractor->init(mr, cache, run_data->buffer); - } -} - -BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, - const ExtractTriBMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LOOPTRI); - - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_looptri_bm(mr, elt, elt_index, run_data->user_data); - } - } - EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; -} - -BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, - const ExtractTriMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LOOPTRI); - - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data->user_data); - } - } - EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; -} - -BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, - const ExtractPolyBMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_POLY); - - EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_poly_bm(mr, f, f_index, run_data->user_data); - } - } - EXTRACT_POLY_FOREACH_BM_END; -} - -BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, - const ExtractPolyMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_POLY); - - EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_poly_mesh(mr, mp, mp_index, run_data->user_data); - } - } - EXTRACT_POLY_FOREACH_MESH_END; -} - -BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, - const ExtractLEdgeBMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LEDGE); - - EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_ledge_bm(mr, eed, ledge_index, run_data->user_data); - } - } - EXTRACT_LEDGE_FOREACH_BM_END; -} - -BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, - const ExtractLEdgeMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LEDGE); - - EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_ledge_mesh(mr, med, ledge_index, run_data->user_data); - } - } - EXTRACT_LEDGE_FOREACH_MESH_END; -} - -BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, - const ExtractLVertBMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LVERT); - - EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_lvert_bm(mr, eve, lvert_index, run_data->user_data); - } - } - EXTRACT_LVERT_FOREACH_BM_END; -} - -BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, - const ExtractLVertMesh_Params *params, - const MeshExtractRunDataArray *_extractors) -{ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - mesh_extract_run_data_array_filter_iter_type(_extractors, &extractors, MR_ITER_LVERT); - - EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) - { - for (int i = 0; i < extractors.len; i++) { - MeshExtractRunData *run_data = &extractors.items[i]; - run_data->extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data->user_data); - } - } - EXTRACT_LVERT_FOREACH_MESH_END; -} - -BLI_INLINE void extract_finish(const MeshRenderData *mr, - struct MeshBatchCache *cache, - const MeshExtractRunDataArray *extractors) -{ - for (int i = 0; i < extractors->len; i++) { - const MeshExtractRunData *run_data = &extractors->items[i]; - const MeshExtract *extractor = run_data->extractor; - if (extractor->finish) { - extractor->finish(mr, cache, run_data->buffer, run_data->user_data); - } - } -} - -/* Single Thread. */ -BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, - eMRIterType iter_type, - MeshBufferCache *mbc) -{ - extract_init(mr, cache, extractors, mbc); - - bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH; - if (iter_type & MR_ITER_LOOPTRI) { - if (is_mesh) { - extract_iter_looptri_mesh(mr, - &(const ExtractTriMesh_Params){ - .mlooptri = mr->mlooptri, - .tri_range = {0, mr->tri_len}, - }, - extractors); - } - else { - extract_iter_looptri_bm(mr, - &(const ExtractTriBMesh_Params){ - .looptris = mr->edit_bmesh->looptris, - .tri_range = {0, mr->tri_len}, - }, - extractors); - } - } - if (iter_type & MR_ITER_POLY) { - if (is_mesh) { - extract_iter_poly_mesh(mr, - &(const ExtractPolyMesh_Params){ - .poly_range = {0, mr->poly_len}, - }, - extractors); - } - else { - extract_iter_poly_bm(mr, - &(const ExtractPolyBMesh_Params){ - .poly_range = {0, mr->poly_len}, - }, - extractors); - } - } - if (iter_type & MR_ITER_LEDGE) { - if (is_mesh) { - extract_iter_ledge_mesh(mr, - &(const ExtractLEdgeMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {0, mr->edge_loose_len}, - }, - extractors); - } - else { - extract_iter_ledge_bm(mr, - &(const ExtractLEdgeBMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {0, mr->edge_loose_len}, - }, - extractors); - } - } - if (iter_type & MR_ITER_LVERT) { - if (is_mesh) { - extract_iter_lvert_mesh(mr, - &(const ExtractLVertMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {0, mr->vert_loose_len}, - }, - extractors); - } - else { - extract_iter_lvert_bm(mr, - &(const ExtractLVertBMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {0, mr->vert_loose_len}, - }, - extractors); - } - } - extract_finish(mr, cache, extractors); -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name ExtractTaskData - * \{ */ -typedef struct ExtractTaskData { - void *next, *prev; - const MeshRenderData *mr; - struct MeshBatchCache *cache; - MeshExtractRunDataArray *extractors; - eMRIterType iter_type; - int start, end; - /** Decremented each time a task is finished. */ - int32_t *task_counter; - MeshBufferCache *mbc; -} ExtractTaskData; - -static ExtractTaskData *extract_extract_iter_task_data_create_mesh( - const MeshRenderData *mr, - struct MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, - MeshBufferCache *mbc, - int32_t *task_counter) -{ - ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), __func__); - taskdata->next = NULL; - taskdata->prev = NULL; - taskdata->mr = mr; - taskdata->cache = cache; - taskdata->mbc = mbc; - - /* #UserData is shared between the iterations as it holds counters to detect if the - * extraction is finished. To make sure the duplication of the user_data does not create a new - * instance of the counters we allocate the user_data in its own container. - * - * This structure makes sure that when extract_init is called, that the user data of all - * iterations are updated. */ - taskdata->extractors = extractors; - taskdata->task_counter = task_counter; - - extracts_flags_get(extractors, &taskdata->iter_type, NULL); - taskdata->start = 0; - taskdata->end = INT_MAX; - return taskdata; -} - -static void extract_task_data_free(void *data) -{ - ExtractTaskData *task_data = data; - MEM_SAFE_FREE(task_data->extractors); - MEM_freeN(task_data); -} - -BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, - const eMRIterType iter_type, - int start, - int end, - MeshExtractRunDataArray *extractors) -{ - switch (mr->extract_type) { - case MR_EXTRACT_BMESH: - if (iter_type & MR_ITER_LOOPTRI) { - extract_iter_looptri_bm(mr, - &(const ExtractTriBMesh_Params){ - .looptris = mr->edit_bmesh->looptris, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_POLY) { - extract_iter_poly_bm(mr, - &(const ExtractPolyBMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_LEDGE) { - extract_iter_ledge_bm(mr, - &(const ExtractLEdgeBMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_LVERT) { - extract_iter_lvert_bm(mr, - &(const ExtractLVertBMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - extractors); - } - break; - case MR_EXTRACT_MAPPED: - case MR_EXTRACT_MESH: - if (iter_type & MR_ITER_LOOPTRI) { - extract_iter_looptri_mesh(mr, - &(const ExtractTriMesh_Params){ - .mlooptri = mr->mlooptri, - .tri_range = {start, min_ii(mr->tri_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_POLY) { - extract_iter_poly_mesh(mr, - &(const ExtractPolyMesh_Params){ - .poly_range = {start, min_ii(mr->poly_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_LEDGE) { - extract_iter_ledge_mesh(mr, - &(const ExtractLEdgeMesh_Params){ - .ledge = mr->ledges, - .ledge_range = {start, min_ii(mr->edge_loose_len, end)}, - }, - extractors); - } - if (iter_type & MR_ITER_LVERT) { - extract_iter_lvert_mesh(mr, - &(const ExtractLVertMesh_Params){ - .lvert = mr->lverts, - .lvert_range = {start, min_ii(mr->vert_loose_len, end)}, - }, - extractors); - } - break; - } -} - -static void extract_task_init(ExtractTaskData *data) -{ - extract_init(data->mr, data->cache, data->extractors, data->mbc); -} - -static void extract_task_run(void *__restrict taskdata) -{ - ExtractTaskData *data = (ExtractTaskData *)taskdata; - mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, data->extractors); - - /* If this is the last task, we do the finish function. */ - int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); - if (remainin_tasks == 0) { - extract_finish(data->mr, data->cache, data->extractors); - } -} - -static void extract_task_init_and_run(void *__restrict taskdata) -{ - ExtractTaskData *data = (ExtractTaskData *)taskdata; - extract_run_and_finish_init(data->mr, data->cache, data->extractors, data->iter_type, data->mbc); -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - Update Mesh Render Data - * \{ */ -typedef struct MeshRenderDataUpdateTaskData { - MeshRenderData *mr; - eMRIterType iter_type; - eMRDataType data_flag; -} MeshRenderDataUpdateTaskData; - -static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata) -{ - BLI_assert(taskdata); - MeshRenderData *mr = taskdata->mr; - mesh_render_data_free(mr); - MEM_freeN(taskdata); -} - -static void mesh_extract_render_data_node_exec(void *__restrict task_data) -{ - MeshRenderDataUpdateTaskData *update_task_data = task_data; - MeshRenderData *mr = update_task_data->mr; - const eMRIterType iter_type = update_task_data->iter_type; - const eMRDataType data_flag = update_task_data->data_flag; - - mesh_render_data_update_normals(mr, iter_type, data_flag); - mesh_render_data_update_looptris(mr, iter_type, data_flag); -} - -static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, - MeshRenderData *mr, - const eMRIterType iter_type, - const eMRDataType data_flag) -{ - MeshRenderDataUpdateTaskData *task_data = MEM_mallocN(sizeof(MeshRenderDataUpdateTaskData), - __func__); - task_data->mr = mr; - task_data->iter_type = iter_type; - task_data->data_flag = data_flag; - - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - mesh_extract_render_data_node_exec, - task_data, - (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - Extract Single Threaded - * \{ */ - -static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph, - ExtractTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - extract_task_init_and_run, - task_data, - (TaskGraphNodeFreeFunction)extract_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Task Node - UserData Initializer - * \{ */ -typedef struct UserDataInitTaskData { - ExtractTaskData *td; - int32_t task_counter; - -} UserDataInitTaskData; - -static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) -{ - BLI_assert(taskdata); - extract_task_data_free(taskdata->td); - MEM_freeN(taskdata); -} - -static void user_data_init_task_data_exec(void *__restrict task_data) -{ - UserDataInitTaskData *extract_task_data = task_data; - ExtractTaskData *taskdata_base = extract_task_data->td; - extract_task_init(taskdata_base); -} - -static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, - UserDataInitTaskData *task_data) -{ - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, - user_data_init_task_data_exec, - task_data, - (TaskGraphNodeFreeFunction)user_data_init_task_data_free); - return task_node; -} - -/** \} */ - -/* ---------------------------------------------------------------------- */ -/** \name Extract Loop - * \{ */ - -static void extract_range_task_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_user_data_init, - ExtractTaskData *taskdata, - const eMRIterType type, - int start, - int length) -{ - taskdata = MEM_dupallocN(taskdata); - atomic_add_and_fetch_int32(taskdata->task_counter, 1); - taskdata->iter_type = type; - taskdata->start = start; - taskdata->end = start + length; - struct TaskNode *task_node = BLI_task_graph_node_create( - task_graph, extract_task_run, taskdata, MEM_freeN); - BLI_task_graph_edge_create(task_node_user_data_init, task_node); -} - -static int extract_range_task_num_elements_get(const MeshRenderData *mr, - const eMRIterType iter_type) -{ - /* Divide task into sensible chunks. */ - int iter_len = 0; - if (iter_type & MR_ITER_LOOPTRI) { - iter_len += mr->tri_len; - } - if (iter_type & MR_ITER_POLY) { - iter_len += mr->poly_len; - } - if (iter_type & MR_ITER_LEDGE) { - iter_len += mr->edge_loose_len; - } - if (iter_type & MR_ITER_LVERT) { - iter_len += mr->vert_loose_len; - } - return iter_len; -} - -static int extract_range_task_chunk_size_get(const MeshRenderData *mr, - const eMRIterType iter_type, - const int num_threads) -{ - /* Divide task into sensible chunks. */ - const int num_elements = extract_range_task_num_elements_get(mr, iter_type); - int range_len = (num_elements + num_threads) / num_threads; - CLAMP_MIN(range_len, CHUNK_SIZE); - return range_len; -} - -static void extract_task_in_ranges_create(struct TaskGraph *task_graph, - struct TaskNode *task_node_user_data_init, - ExtractTaskData *taskdata_base, - const int num_threads) -{ - const MeshRenderData *mr = taskdata_base->mr; - const int range_len = extract_range_task_chunk_size_get( - mr, taskdata_base->iter_type, num_threads); - - if (taskdata_base->iter_type & MR_ITER_LOOPTRI) { - for (int i = 0; i < mr->tri_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len); - } - } - if (taskdata_base->iter_type & MR_ITER_POLY) { - for (int i = 0; i < mr->poly_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len); - } - } - if (taskdata_base->iter_type & MR_ITER_LEDGE) { - for (int i = 0; i < mr->edge_loose_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len); - } - } - if (taskdata_base->iter_type & MR_ITER_LVERT) { - for (int i = 0; i < mr->vert_loose_len; i += range_len) { - extract_range_task_create( - task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len); - } - } -} - -void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, - MeshBatchCache *cache, - MeshBufferCache *mbc, - MeshBufferExtractionCache *extraction_cache, - Mesh *me, - - const bool is_editmode, - const bool is_paint_mode, - const bool is_mode_active, - const float obmat[4][4], - const bool do_final, - const bool do_uvedit, - const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, - const Scene *scene, - const ToolSettings *ts, - const bool use_hide) -{ - /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. - * This sub-graph starts with an extract_render_data_node. This fills/converts the required - * data from Mesh. - * - * Small extractions and extractions that can't be multi-threaded are grouped in a single - * `extract_single_threaded_task_node`. - * - * Other extractions will create a node for each loop exceeding 8192 items. these nodes are - * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the - * user_data needed for the extraction based on the data extracted from the mesh. - * counters are used to check if the finalize of a task has to be called. - * - * Mesh extraction sub graph - * - * +----------------------+ - * +-----> | extract_task1_loop_1 | - * | +----------------------+ - * +------------------+ +----------------------+ +----------------------+ - * | mesh_render_data | --> | | --> | extract_task1_loop_2 | - * +------------------+ | | +----------------------+ - * | | | +----------------------+ - * | | user_data_init | --> | extract_task2_loop_1 | - * v | | +----------------------+ - * +------------------+ | | +----------------------+ - * | single_threaded | | | --> | extract_task2_loop_2 | - * +------------------+ +----------------------+ +----------------------+ - * | +----------------------+ - * +-----> | extract_task2_loop_3 | - * +----------------------+ - */ - const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != NULL; - const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || - GPU_use_hq_normals_workaround(); - - /* Create an array containing all the extractors that needs to be executed. */ - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_init(&extractors); - -#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ - do { \ - if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ - const MeshExtract *extractor = mesh_extract_override_get( \ - &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ - mesh_extract_run_data_array_add(&extractors, extractor); \ - } \ - } while (0) - - EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor); - EXTRACT_ADD_REQUESTED(VBO, vbo, lnor); - EXTRACT_ADD_REQUESTED(VBO, vbo, uv); - EXTRACT_ADD_REQUESTED(VBO, vbo, tan); - EXTRACT_ADD_REQUESTED(VBO, vbo, vcol); - EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data); - EXTRACT_ADD_REQUESTED(VBO, vbo, orco); - EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac); - EXTRACT_ADD_REQUESTED(VBO, vbo, weights); - EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data); - EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data); - EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area); - EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle); - EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data); - EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx); - EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx); - EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx); - EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx); - EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots); - - EXTRACT_ADD_REQUESTED(IBO, ibo, tris); - EXTRACT_ADD_REQUESTED(IBO, ibo, lines); - EXTRACT_ADD_REQUESTED(IBO, ibo, points); - EXTRACT_ADD_REQUESTED(IBO, ibo, fdots); - EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask); - EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency); - EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris); - EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines); - EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points); - EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots); - -#undef EXTRACT_ADD_REQUESTED - - if (extractors.len == 0) { - return; - } - -#ifdef DEBUG_TIME - double rdata_start = PIL_check_seconds_timer(); -#endif - - eMRIterType iter_type; - eMRDataType data_flag; - extracts_flags_get(&extractors, &iter_type, &data_flag); - - MeshRenderData *mr = mesh_render_data_create(me, - extraction_cache, - is_editmode, - is_paint_mode, - is_mode_active, - obmat, - do_final, - do_uvedit, - cd_layer_used, - ts, - iter_type); - mr->use_hide = use_hide; - mr->use_subsurf_fdots = use_subsurf_fdots; - mr->use_final_mesh = do_final; - -#ifdef DEBUG_TIME - double rdata_end = PIL_check_seconds_timer(); -#endif - - struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( - task_graph, mr, iter_type, data_flag); - - /* Simple heuristic. */ - const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; - - if (use_thread) { - uint threads_to_use = 0; - - /* First run the requested extractors that do not support asynchronous ranges. */ - for (int i = 0; i < extractors.len; i++) { - const MeshExtract *extractor = extractors.items[i].extractor; - if (!extractor->use_threading) { - MeshExtractRunDataArray *single_threaded_extractors = MEM_callocN( - sizeof(MeshExtractRunDataArray), - "mesh_buffer_cache_create_requested.single_threaded_extractors"); - mesh_extract_run_data_array_add(single_threaded_extractors, extractor); - ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( - mr, cache, single_threaded_extractors, mbc, NULL); - struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, - taskdata); - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); - } - threads_to_use++; - } - - /* Distribute the remaining extractors into ranges per core. */ - MeshExtractRunDataArray *multi_threaded_extractors = MEM_callocN( - sizeof(MeshExtractRunDataArray), - "mesh_buffer_cache_create_requested.multi_threaded_extractors"); - mesh_extract_run_data_array_filter_threading(&extractors, multi_threaded_extractors); - if (multi_threaded_extractors->len) { - /* - * Determine the number of thread to use for multithreading. - * Thread can be used for single threaded tasks. These typically take longer to execute so - * fill the rest of the threads for range operations. - */ - int num_threads = BLI_task_scheduler_num_threads(); - if (threads_to_use < num_threads) { - num_threads -= threads_to_use; - } - - UserDataInitTaskData *user_data_init_task_data = MEM_callocN( - sizeof(UserDataInitTaskData), - "mesh_buffer_cache_create_requested.user_data_init_task_data"); - struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( - task_graph, user_data_init_task_data); - - user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh( - mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter); - - extract_task_in_ranges_create( - task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads); - - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); - } - else { - /* No tasks created freeing extractors list. */ - MEM_freeN(multi_threaded_extractors); - } - } - else { - /* Run all requests on the same thread. */ - MeshExtractRunDataArray *extractors_copy = MEM_mallocN( - sizeof(MeshExtractRunDataArray), "mesh_buffer_cache_create_requested.extractors_copy"); - memcpy(extractors_copy, &extractors, sizeof(MeshExtractRunDataArray)); - ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( - mr, cache, extractors_copy, mbc, NULL); - - struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); - BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); - } - - /* Trigger the sub-graph for this mesh. */ - BLI_task_graph_node_push_work(task_node_mesh_render_data); - -#ifdef DEBUG_TIME - BLI_task_graph_work_and_wait(task_graph); - double end = PIL_check_seconds_timer(); - - static double avg = 0; - static double avg_fps = 0; - static double avg_rdata = 0; - static double end_prev = 0; - - if (end_prev == 0) { - end_prev = end; - } - - avg = avg * 0.95 + (end - rdata_end) * 0.05; - avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05; - avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05; - - printf( - "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000); - - end_prev = end; -#endif -} - -/** \} */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc new file mode 100644 index 00000000000..b3ebce98524 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -0,0 +1,1006 @@ +/* + * 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) 2017 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + * + * \brief Extraction of Mesh data into VBO to feed to GPU. + */ +#include "MEM_guardedalloc.h" + +#include "atomic_ops.h" + +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" + +#include "BLI_task.h" +#include "BLI_vector.hh" + +#include "BKE_editmesh.h" + +#include "GPU_capabilities.h" + +#include "draw_cache_extract.h" +#include "draw_cache_extract_mesh_private.h" +#include "draw_cache_inline.h" + +// #define DEBUG_TIME + +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#endif + +#define CHUNK_SIZE 8192 + +namespace blender::draw { + +/* ---------------------------------------------------------------------- */ +/** \name Mesh Elements Extract Struct + * \{ */ + +struct MeshExtractRunData { + const MeshExtract *extractor; + void *buffer; + void *user_data; +}; + +using MeshExtractRunDataArray = blender::Vector; + +static void mesh_extract_run_data_array_add(MeshExtractRunDataArray &array, + const MeshExtract *extractor) +{ + MeshExtractRunData run_data; + run_data.extractor = extractor; + run_data.buffer = NULL; + run_data.user_data = NULL; + array.append(run_data); +} + +static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray &src, + MeshExtractRunDataArray &dst, + eMRIterType iter_type) +{ + for (const MeshExtractRunData &data : src) { + const MeshExtract *extractor = data.extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + dst.append(data); + continue; + } + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + dst.append(data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + dst.append(data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + dst.append(data); + continue; + } + } +} + +static void mesh_extract_run_data_array_filter_threading( + const MeshExtractRunDataArray &src, MeshExtractRunDataArray &dst_multi_threaded) +{ + for (const MeshExtractRunData &data : src) { + const MeshExtract *extractor = data.extractor; + if (extractor->use_threading) { + mesh_extract_run_data_array_add(dst_multi_threaded, extractor); + } + } +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract + * \{ */ + +static void extracts_flags_get(const MeshExtractRunDataArray &extractors, + eMRIterType *r_iter_type, + eMRDataType *r_data_flag) +{ + eMRIterType iter_type = static_cast(0); + eMRDataType data_flag = static_cast(0); + + for (const MeshExtractRunData &data : extractors) { + const MeshExtract *extractor = data.extractor; + iter_type |= mesh_extract_iter_type(extractor); + data_flag |= extractor->data_flag; + } + + if (r_iter_type) { + *r_iter_type = iter_type; + } + if (r_data_flag) { + *r_data_flag = data_flag; + } +} + +BLI_INLINE void extract_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray &extractors, + MeshBufferCache *mbc) +{ + /* Multi thread. */ + for (MeshExtractRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + run_data.buffer = mesh_extract_buffer_get(extractor, mbc); + run_data.user_data = extractor->init(mr, cache, run_data.buffer); + } +} + +BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, + const ExtractTriBMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data.user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, + const ExtractTriMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LOOPTRI); + + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data.user_data); + } + } + EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, + const ExtractPolyBMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_poly_bm(mr, f, f_index, run_data.user_data); + } + } + EXTRACT_POLY_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, + const ExtractPolyMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_POLY); + + EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data.user_data); + } + } + EXTRACT_POLY_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, + const ExtractLEdgeBMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data.user_data); + } + } + EXTRACT_LEDGE_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, + const ExtractLEdgeMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LEDGE); + + EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data.user_data); + } + } + EXTRACT_LEDGE_FOREACH_MESH_END; +} + +BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, + const ExtractLVertBMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data.user_data); + } + } + EXTRACT_LVERT_FOREACH_BM_END; +} + +BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, + const ExtractLVertMesh_Params *params, + const MeshExtractRunDataArray &all_extractors) +{ + MeshExtractRunDataArray extractors; + mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LVERT); + + EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) + { + for (MeshExtractRunData &run_data : extractors) { + run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data.user_data); + } + } + EXTRACT_LVERT_FOREACH_MESH_END; +} + +BLI_INLINE void extract_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + const MeshExtractRunDataArray &extractors) +{ + for (const MeshExtractRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + if (extractor->finish) { + extractor->finish(mr, cache, run_data.buffer, run_data.user_data); + } + } +} + +/* Single Thread. */ +BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray &extractors, + eMRIterType iter_type, + MeshBufferCache *mbc) +{ + extract_init(mr, cache, extractors, mbc); + + bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH; + if (iter_type & MR_ITER_LOOPTRI) { + if (is_mesh) { + ExtractTriMesh_Params params; + params.mlooptri = mr->mlooptri; + params.tri_range[0] = 0; + params.tri_range[1] = mr->tri_len; + extract_iter_looptri_mesh(mr, ¶ms, extractors); + } + else { + ExtractTriBMesh_Params params; + params.looptris = mr->edit_bmesh->looptris; + params.tri_range[0] = 0; + params.tri_range[1] = mr->tri_len; + extract_iter_looptri_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_POLY) { + if (is_mesh) { + ExtractPolyMesh_Params params; + params.poly_range[0] = 0; + params.poly_range[1] = mr->poly_len; + extract_iter_poly_mesh(mr, ¶ms, extractors); + } + else { + ExtractPolyBMesh_Params params; + params.poly_range[0] = 0; + params.poly_range[1] = mr->poly_len; + extract_iter_poly_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_LEDGE) { + if (is_mesh) { + ExtractLEdgeMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = 0; + params.ledge_range[1] = mr->edge_loose_len; + extract_iter_ledge_mesh(mr, ¶ms, extractors); + } + else { + ExtractLEdgeBMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = 0; + params.ledge_range[1] = mr->edge_loose_len; + extract_iter_ledge_bm(mr, ¶ms, extractors); + } + } + if (iter_type & MR_ITER_LVERT) { + if (is_mesh) { + ExtractLVertMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = 0; + params.lvert_range[1] = mr->vert_loose_len; + extract_iter_lvert_mesh(mr, ¶ms, extractors); + } + else { + ExtractLVertBMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = 0; + params.lvert_range[1] = mr->vert_loose_len; + extract_iter_lvert_bm(mr, ¶ms, extractors); + } + } + extract_finish(mr, cache, extractors); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name ExtractTaskData + * \{ */ +struct ExtractTaskData { + void *next = nullptr; + void *prev = nullptr; + + const MeshRenderData *mr = nullptr; + MeshBatchCache *cache = nullptr; + /* #UserData is shared between the iterations as it holds counters to detect if the + * extraction is finished. To make sure the duplication of the user_data does not create a new + * instance of the counters we allocate the user_data in its own container. + * + * This structure makes sure that when extract_init is called, that the user data of all + * iterations are updated. */ + + MeshExtractRunDataArray *extractors = nullptr; + MeshBufferCache *mbc = nullptr; + int32_t *task_counter = nullptr; + + eMRIterType iter_type; + int start = 0; + int end = INT_MAX; + /** Decremented each time a task is finished. */ + + ExtractTaskData(const MeshRenderData *mr, + struct MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) + : mr(mr), cache(cache), extractors(extractors), mbc(mbc), task_counter(task_counter) + { + extracts_flags_get(*extractors, &iter_type, NULL); + }; + + ExtractTaskData(const ExtractTaskData &src) = default; + + ~ExtractTaskData() + { + delete extractors; + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:ExtractTaskData") +#endif +}; + +static ExtractTaskData *extract_extract_iter_task_data_create_mesh( + const MeshRenderData *mr, + MeshBatchCache *cache, + MeshExtractRunDataArray *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) + +{ + ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, task_counter); + return taskdata; +} + +static void extract_task_data_free(void *data) +{ + ExtractTaskData *task_data = static_cast(data); + delete task_data; +} + +static void extract_task_data_free_ex(void *data) +{ + ExtractTaskData *task_data = static_cast(data); + task_data->extractors = nullptr; + delete task_data; +} + +BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, + const eMRIterType iter_type, + int start, + int end, + MeshExtractRunDataArray &extractors) +{ + switch (mr->extract_type) { + case MR_EXTRACT_BMESH: + if (iter_type & MR_ITER_LOOPTRI) { + ExtractTriBMesh_Params params; + params.looptris = mr->edit_bmesh->looptris; + params.tri_range[0] = start; + params.tri_range[1] = min_ii(mr->tri_len, end); + extract_iter_looptri_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_POLY) { + ExtractPolyBMesh_Params params; + params.poly_range[0] = start; + params.poly_range[1] = min_ii(mr->poly_len, end); + extract_iter_poly_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LEDGE) { + ExtractLEdgeBMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = start; + params.ledge_range[1] = min_ii(mr->edge_loose_len, end); + extract_iter_ledge_bm(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LVERT) { + ExtractLVertBMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = start; + params.lvert_range[1] = min_ii(mr->vert_loose_len, end); + extract_iter_lvert_bm(mr, ¶ms, extractors); + } + break; + case MR_EXTRACT_MAPPED: + case MR_EXTRACT_MESH: + if (iter_type & MR_ITER_LOOPTRI) { + ExtractTriMesh_Params params; + params.mlooptri = mr->mlooptri; + params.tri_range[0] = start; + params.tri_range[1] = min_ii(mr->tri_len, end); + extract_iter_looptri_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_POLY) { + ExtractPolyMesh_Params params; + params.poly_range[0] = start; + params.poly_range[1] = min_ii(mr->poly_len, end); + extract_iter_poly_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LEDGE) { + ExtractLEdgeMesh_Params params; + params.ledge = mr->ledges; + params.ledge_range[0] = start; + params.ledge_range[1] = min_ii(mr->edge_loose_len, end); + extract_iter_ledge_mesh(mr, ¶ms, extractors); + } + if (iter_type & MR_ITER_LVERT) { + ExtractLVertMesh_Params params; + params.lvert = mr->lverts; + params.lvert_range[0] = start; + params.lvert_range[1] = min_ii(mr->vert_loose_len, end); + extract_iter_lvert_mesh(mr, ¶ms, extractors); + } + break; + } +} + +static void extract_task_init(ExtractTaskData *data) +{ + extract_init(data->mr, data->cache, *data->extractors, data->mbc); +} + +static void extract_task_run(void *__restrict taskdata) +{ + ExtractTaskData *data = (ExtractTaskData *)taskdata; + mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, *data->extractors); + + /* If this is the last task, we do the finish function. */ + int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); + if (remainin_tasks == 0) { + extract_finish(data->mr, data->cache, *data->extractors); + } +} + +static void extract_task_init_and_run(void *__restrict taskdata) +{ + ExtractTaskData *data = (ExtractTaskData *)taskdata; + extract_run_and_finish_init( + data->mr, data->cache, *data->extractors, data->iter_type, data->mbc); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - Update Mesh Render Data + * \{ */ +struct MeshRenderDataUpdateTaskData { + MeshRenderData *mr = nullptr; + eMRIterType iter_type; + eMRDataType data_flag; + + ~MeshRenderDataUpdateTaskData() + { + mesh_render_data_free(mr); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData") +#endif +}; + +static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata) +{ + BLI_assert(taskdata); + delete taskdata; +} + +static void mesh_extract_render_data_node_exec(void *__restrict task_data) +{ + MeshRenderDataUpdateTaskData *update_task_data = static_cast( + task_data); + MeshRenderData *mr = update_task_data->mr; + const eMRIterType iter_type = update_task_data->iter_type; + const eMRDataType data_flag = update_task_data->data_flag; + + mesh_render_data_update_normals(mr, iter_type, data_flag); + mesh_render_data_update_looptris(mr, iter_type, data_flag); +} + +static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, + MeshRenderData *mr, + const eMRIterType iter_type, + const eMRDataType data_flag) +{ + MeshRenderDataUpdateTaskData *task_data = new (MeshRenderDataUpdateTaskData); + task_data->mr = mr; + task_data->iter_type = iter_type; + task_data->data_flag = data_flag; + + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + mesh_extract_render_data_node_exec, + task_data, + (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - Extract Single Threaded + * \{ */ + +static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph, + ExtractTaskData *task_data) +{ + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + extract_task_init_and_run, + task_data, + (TaskGraphNodeFreeFunction)extract_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Task Node - UserData Initializer + * \{ */ +struct UserDataInitTaskData { + ExtractTaskData *td; + int32_t task_counter = 0; + + ~UserDataInitTaskData() + { + extract_task_data_free(td); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("DRW:UserDataInitTaskData") +#endif +}; + +static void user_data_init_task_data_free(UserDataInitTaskData *taskdata) +{ + delete taskdata; +} + +static void user_data_init_task_data_exec(void *__restrict task_data) +{ + UserDataInitTaskData *extract_task_data = static_cast(task_data); + ExtractTaskData *taskdata_base = extract_task_data->td; + extract_task_init(taskdata_base); +} + +static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph, + UserDataInitTaskData *task_data) +{ + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, + user_data_init_task_data_exec, + task_data, + (TaskGraphNodeFreeFunction)user_data_init_task_data_free); + return task_node; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Extract Loop + * \{ */ + +static void extract_range_task_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata, + const eMRIterType type, + int start, + int length) +{ + taskdata = new ExtractTaskData(*taskdata); + atomic_add_and_fetch_int32(taskdata->task_counter, 1); + taskdata->iter_type = type; + taskdata->start = start; + taskdata->end = start + length; + struct TaskNode *task_node = BLI_task_graph_node_create( + task_graph, extract_task_run, taskdata, extract_task_data_free_ex); + BLI_task_graph_edge_create(task_node_user_data_init, task_node); +} + +static int extract_range_task_num_elements_get(const MeshRenderData *mr, + const eMRIterType iter_type) +{ + /* Divide task into sensible chunks. */ + int iter_len = 0; + if (iter_type & MR_ITER_LOOPTRI) { + iter_len += mr->tri_len; + } + if (iter_type & MR_ITER_POLY) { + iter_len += mr->poly_len; + } + if (iter_type & MR_ITER_LEDGE) { + iter_len += mr->edge_loose_len; + } + if (iter_type & MR_ITER_LVERT) { + iter_len += mr->vert_loose_len; + } + return iter_len; +} + +static int extract_range_task_chunk_size_get(const MeshRenderData *mr, + const eMRIterType iter_type, + const int num_threads) +{ + /* Divide task into sensible chunks. */ + const int num_elements = extract_range_task_num_elements_get(mr, iter_type); + int range_len = (num_elements + num_threads) / num_threads; + CLAMP_MIN(range_len, CHUNK_SIZE); + return range_len; +} + +static void extract_task_in_ranges_create(struct TaskGraph *task_graph, + struct TaskNode *task_node_user_data_init, + ExtractTaskData *taskdata_base, + const int num_threads) +{ + const MeshRenderData *mr = taskdata_base->mr; + const int range_len = extract_range_task_chunk_size_get( + mr, taskdata_base->iter_type, num_threads); + + if (taskdata_base->iter_type & MR_ITER_LOOPTRI) { + for (int i = 0; i < mr->tri_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_POLY) { + for (int i = 0; i < mr->poly_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_LEDGE) { + for (int i = 0; i < mr->edge_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len); + } + } + if (taskdata_base->iter_type & MR_ITER_LVERT) { + for (int i = 0; i < mr->vert_loose_len; i += range_len) { + extract_range_task_create( + task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len); + } + } +} + +static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, + MeshBatchCache *cache, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, + Mesh *me, + + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const DRW_MeshCDMask *cd_layer_used, + const Scene *scene, + const ToolSettings *ts, + const bool use_hide) +{ + /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph. + * This sub-graph starts with an extract_render_data_node. This fills/converts the required + * data from Mesh. + * + * Small extractions and extractions that can't be multi-threaded are grouped in a single + * `extract_single_threaded_task_node`. + * + * Other extractions will create a node for each loop exceeding 8192 items. these nodes are + * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the + * user_data needed for the extraction based on the data extracted from the mesh. + * counters are used to check if the finalize of a task has to be called. + * + * Mesh extraction sub graph + * + * +----------------------+ + * +-----> | extract_task1_loop_1 | + * | +----------------------+ + * +------------------+ +----------------------+ +----------------------+ + * | mesh_render_data | --> | | --> | extract_task1_loop_2 | + * +------------------+ | | +----------------------+ + * | | | +----------------------+ + * | | user_data_init | --> | extract_task2_loop_1 | + * v | | +----------------------+ + * +------------------+ | | +----------------------+ + * | single_threaded | | | --> | extract_task2_loop_2 | + * +------------------+ +----------------------+ +----------------------+ + * | +----------------------+ + * +-----> | extract_task2_loop_3 | + * +----------------------+ + */ + const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != NULL; + const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || + GPU_use_hq_normals_workaround(); + + /* Create an array containing all the extractors that needs to be executed. */ + MeshExtractRunDataArray extractors; + +#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ + do { \ + if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ + const MeshExtract *extractor = mesh_extract_override_get( \ + &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ + mesh_extract_run_data_array_add(extractors, extractor); \ + } \ + } while (0) + + EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, lnor); + EXTRACT_ADD_REQUESTED(VBO, vbo, uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, tan); + EXTRACT_ADD_REQUESTED(VBO, vbo, vcol); + EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, orco); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac); + EXTRACT_ADD_REQUESTED(VBO, vbo, weights); + EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area); + EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle); + EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data); + EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx); + EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots); + + EXTRACT_ADD_REQUESTED(IBO, ibo, tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, points); + EXTRACT_ADD_REQUESTED(IBO, ibo, fdots); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask); + EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points); + EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots); + +#undef EXTRACT_ADD_REQUESTED + + if (extractors.is_empty()) { + return; + } + +#ifdef DEBUG_TIME + double rdata_start = PIL_check_seconds_timer(); +#endif + + eMRIterType iter_type; + eMRDataType data_flag; + extracts_flags_get(extractors, &iter_type, &data_flag); + + MeshRenderData *mr = mesh_render_data_create(me, + extraction_cache, + is_editmode, + is_paint_mode, + is_mode_active, + obmat, + do_final, + do_uvedit, + cd_layer_used, + ts, + iter_type); + mr->use_hide = use_hide; + mr->use_subsurf_fdots = use_subsurf_fdots; + mr->use_final_mesh = do_final; + +#ifdef DEBUG_TIME + double rdata_end = PIL_check_seconds_timer(); +#endif + + struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( + task_graph, mr, iter_type, data_flag); + + /* Simple heuristic. */ + const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; + + if (use_thread) { + uint threads_to_use = 0; + + /* First run the requested extractors that do not support asynchronous ranges. */ + for (const MeshExtractRunData &run_data : extractors) { + const MeshExtract *extractor = run_data.extractor; + if (!extractor->use_threading) { + MeshExtractRunDataArray *single_threaded_extractors = new MeshExtractRunDataArray(); + mesh_extract_run_data_array_add(*single_threaded_extractors, extractor); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, single_threaded_extractors, mbc, NULL); + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, + taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + threads_to_use++; + } + + /* Distribute the remaining extractors into ranges per core. */ + MeshExtractRunDataArray *multi_threaded_extractors = new MeshExtractRunDataArray(); + mesh_extract_run_data_array_filter_threading(extractors, *multi_threaded_extractors); + if (!multi_threaded_extractors->is_empty()) { + /* + * Determine the number of thread to use for multithreading. + * Thread can be used for single threaded tasks. These typically take longer to execute so + * fill the rest of the threads for range operations. + */ + int num_threads = BLI_task_scheduler_num_threads(); + if (threads_to_use < num_threads) { + num_threads -= threads_to_use; + } + + UserDataInitTaskData *user_data_init_task_data = new UserDataInitTaskData(); + struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( + task_graph, user_data_init_task_data); + + user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh( + mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter); + + extract_task_in_ranges_create( + task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads); + + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init); + } + else { + /* No tasks created freeing extractors list. */ + delete multi_threaded_extractors; + } + } + else { + /* Run all requests on the same thread. */ + MeshExtractRunDataArray *extractors_copy = new MeshExtractRunDataArray(extractors); + ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( + mr, cache, extractors_copy, mbc, NULL); + + struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); + BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + } + + /* Trigger the sub-graph for this mesh. */ + BLI_task_graph_node_push_work(task_node_mesh_render_data); + +#ifdef DEBUG_TIME + BLI_task_graph_work_and_wait(task_graph); + double end = PIL_check_seconds_timer(); + + static double avg = 0; + static double avg_fps = 0; + static double avg_rdata = 0; + static double end_prev = 0; + + if (end_prev == 0) { + end_prev = end; + } + + avg = avg * 0.95 + (end - rdata_end) * 0.05; + avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05; + avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05; + + printf( + "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000); + + end_prev = end; +#endif +} + +} // namespace blender::draw + +extern "C" { +void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, + MeshBatchCache *cache, + MeshBufferCache *mbc, + MeshBufferExtractionCache *extraction_cache, + Mesh *me, + + const bool is_editmode, + const bool is_paint_mode, + const bool is_mode_active, + const float obmat[4][4], + const bool do_final, + const bool do_uvedit, + const bool use_subsurf_fdots, + const DRW_MeshCDMask *cd_layer_used, + const Scene *scene, + const ToolSettings *ts, + const bool use_hide) +{ + blender::draw::mesh_buffer_cache_create_requested(task_graph, + cache, + mbc, + extraction_cache, + me, + is_editmode, + is_paint_mode, + is_mode_active, + obmat, + do_final, + do_uvedit, + use_subsurf_fdots, + cd_layer_used, + scene, + ts, + use_hide); +} + +} // extern "C" + +/** \} */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index 3360b90139d..6dd2ef4c16f 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -34,6 +34,10 @@ #include "draw_cache_extract.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum eMRExtractType { MR_EXTRACT_BMESH, MR_EXTRACT_MAPPED, @@ -507,3 +511,7 @@ extern const MeshExtract extract_poly_idx; extern const MeshExtract extract_edge_idx; extern const MeshExtract extract_vert_idx; extern const MeshExtract extract_fdot_idx; + +#ifdef __cplusplus +} +#endif -- cgit v1.2.3 From f0d93a71df85e91ec4e06d2b37e24fb728edf2c2 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 12:59:15 +0200 Subject: Cleanup: API for MeshExtractRunTimeData. --- .../blender/draw/intern/draw_cache_extract_mesh.cc | 246 ++++++++++----------- .../intern/draw_cache_extract_mesh_extractors.c | 76 +++---- .../draw/intern/draw_cache_extract_mesh_private.h | 2 +- 3 files changed, 159 insertions(+), 165 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index b3ebce98524..5a086805580 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -54,63 +54,80 @@ namespace blender::draw { /** \name Mesh Elements Extract Struct * \{ */ -struct MeshExtractRunData { +struct ExtractorRunData { + /* Extractor where this run data belongs to. */ const MeshExtract *extractor; - void *buffer; - void *user_data; -}; - -using MeshExtractRunDataArray = blender::Vector; + /* During iteration the VBO/IBO that is being build. */ + void *buffer = nullptr; + /* User data during iteration. Created in MeshExtract.init and passed along to other MeshExtract + * functions. */ + void *user_data = nullptr; -static void mesh_extract_run_data_array_add(MeshExtractRunDataArray &array, - const MeshExtract *extractor) -{ - MeshExtractRunData run_data; - run_data.extractor = extractor; - run_data.buffer = NULL; - run_data.user_data = NULL; - array.append(run_data); -} + ExtractorRunData(const MeshExtract *extractor) : extractor(extractor) + { + } +}; -static void mesh_extract_run_data_array_filter_iter_type(const MeshExtractRunDataArray &src, - MeshExtractRunDataArray &dst, - eMRIterType iter_type) -{ - for (const MeshExtractRunData &data : src) { - const MeshExtract *extractor = data.extractor; - if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { - BLI_assert(extractor->iter_looptri_mesh); - dst.append(data); - continue; - } - if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { - BLI_assert(extractor->iter_poly_mesh); - dst.append(data); - continue; +class ExtractorRunDatas : public Vector { + public: + void filter_into(ExtractorRunDatas &result, eMRIterType iter_type) const + { + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { + BLI_assert(extractor->iter_looptri_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { + BLI_assert(extractor->iter_poly_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { + BLI_assert(extractor->iter_ledge_mesh); + result.append(data); + continue; + } + if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { + BLI_assert(extractor->iter_lvert_mesh); + result.append(data); + continue; + } } - if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { - BLI_assert(extractor->iter_ledge_mesh); - dst.append(data); - continue; + } + + void filter_threaded_extractors_into(ExtractorRunDatas &result) + { + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + if (extractor->use_threading) { + result.append(extractor); + } } - if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { - BLI_assert(extractor->iter_lvert_mesh); - dst.append(data); - continue; + } + + eMRIterType iter_types() + { + eMRIterType iter_type = static_cast(0); + + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + iter_type |= mesh_extract_iter_type(extractor); } + return iter_type; } -} -static void mesh_extract_run_data_array_filter_threading( - const MeshExtractRunDataArray &src, MeshExtractRunDataArray &dst_multi_threaded) -{ - for (const MeshExtractRunData &data : src) { - const MeshExtract *extractor = data.extractor; - if (extractor->use_threading) { - mesh_extract_run_data_array_add(dst_multi_threaded, extractor); + eMRDataType data_types() + { + eMRDataType data_type = static_cast(0); + for (const ExtractorRunData &data : *this) { + const MeshExtract *extractor = data.extractor; + data_type |= extractor->data_type; } + return data_type; } -} +}; /** \} */ @@ -118,34 +135,13 @@ static void mesh_extract_run_data_array_filter_threading( /** \name Extract * \{ */ -static void extracts_flags_get(const MeshExtractRunDataArray &extractors, - eMRIterType *r_iter_type, - eMRDataType *r_data_flag) -{ - eMRIterType iter_type = static_cast(0); - eMRDataType data_flag = static_cast(0); - - for (const MeshExtractRunData &data : extractors) { - const MeshExtract *extractor = data.extractor; - iter_type |= mesh_extract_iter_type(extractor); - data_flag |= extractor->data_flag; - } - - if (r_iter_type) { - *r_iter_type = iter_type; - } - if (r_data_flag) { - *r_data_flag = data_flag; - } -} - BLI_INLINE void extract_init(const MeshRenderData *mr, struct MeshBatchCache *cache, - MeshExtractRunDataArray &extractors, + ExtractorRunDatas &extractors, MeshBufferCache *mbc) { /* Multi thread. */ - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { const MeshExtract *extractor = run_data.extractor; run_data.buffer = mesh_extract_buffer_get(extractor, mbc); run_data.user_data = extractor->init(mr, cache, run_data.buffer); @@ -154,14 +150,14 @@ BLI_INLINE void extract_init(const MeshRenderData *mr, BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, const ExtractTriBMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LOOPTRI); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data.user_data); } } @@ -170,14 +166,14 @@ BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr, BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, const ExtractTriMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LOOPTRI); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LOOPTRI); EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data.user_data); } } @@ -186,14 +182,14 @@ BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr, BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, const ExtractPolyBMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_POLY); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_POLY); EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_poly_bm(mr, f, f_index, run_data.user_data); } } @@ -202,14 +198,14 @@ BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr, BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, const ExtractPolyMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_POLY); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_POLY); EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data.user_data); } } @@ -218,14 +214,14 @@ BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr, BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, const ExtractLEdgeBMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LEDGE); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LEDGE); EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data.user_data); } } @@ -234,14 +230,14 @@ BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr, BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, const ExtractLEdgeMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LEDGE); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LEDGE); EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data.user_data); } } @@ -250,14 +246,14 @@ BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr, BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, const ExtractLVertBMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LVERT); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LVERT); EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data.user_data); } } @@ -266,14 +262,14 @@ BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr, BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, const ExtractLVertMesh_Params *params, - const MeshExtractRunDataArray &all_extractors) + const ExtractorRunDatas &all_extractors) { - MeshExtractRunDataArray extractors; - mesh_extract_run_data_array_filter_iter_type(all_extractors, extractors, MR_ITER_LVERT); + ExtractorRunDatas extractors; + all_extractors.filter_into(extractors, MR_ITER_LVERT); EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr) { - for (MeshExtractRunData &run_data : extractors) { + for (ExtractorRunData &run_data : extractors) { run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data.user_data); } } @@ -282,9 +278,9 @@ BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr, BLI_INLINE void extract_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, - const MeshExtractRunDataArray &extractors) + const ExtractorRunDatas &extractors) { - for (const MeshExtractRunData &run_data : extractors) { + for (const ExtractorRunData &run_data : extractors) { const MeshExtract *extractor = run_data.extractor; if (extractor->finish) { extractor->finish(mr, cache, run_data.buffer, run_data.user_data); @@ -295,7 +291,7 @@ BLI_INLINE void extract_finish(const MeshRenderData *mr, /* Single Thread. */ BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, struct MeshBatchCache *cache, - MeshExtractRunDataArray &extractors, + ExtractorRunDatas &extractors, eMRIterType iter_type, MeshBufferCache *mbc) { @@ -385,7 +381,7 @@ struct ExtractTaskData { * This structure makes sure that when extract_init is called, that the user data of all * iterations are updated. */ - MeshExtractRunDataArray *extractors = nullptr; + ExtractorRunDatas *extractors = nullptr; MeshBufferCache *mbc = nullptr; int32_t *task_counter = nullptr; @@ -396,12 +392,12 @@ struct ExtractTaskData { ExtractTaskData(const MeshRenderData *mr, struct MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, + ExtractorRunDatas *extractors, MeshBufferCache *mbc, int32_t *task_counter) : mr(mr), cache(cache), extractors(extractors), mbc(mbc), task_counter(task_counter) { - extracts_flags_get(*extractors, &iter_type, NULL); + iter_type = extractors->iter_types(); }; ExtractTaskData(const ExtractTaskData &src) = default; @@ -416,12 +412,11 @@ struct ExtractTaskData { #endif }; -static ExtractTaskData *extract_extract_iter_task_data_create_mesh( - const MeshRenderData *mr, - MeshBatchCache *cache, - MeshExtractRunDataArray *extractors, - MeshBufferCache *mbc, - int32_t *task_counter) +static ExtractTaskData *extract_extract_iter_task_data_create_mesh(const MeshRenderData *mr, + MeshBatchCache *cache, + ExtractorRunDatas *extractors, + MeshBufferCache *mbc, + int32_t *task_counter) { ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, task_counter); @@ -445,7 +440,7 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, const eMRIterType iter_type, int start, int end, - MeshExtractRunDataArray &extractors) + ExtractorRunDatas &extractors) { switch (mr->extract_type) { case MR_EXTRACT_BMESH: @@ -791,14 +786,14 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, GPU_use_hq_normals_workaround(); /* Create an array containing all the extractors that needs to be executed. */ - MeshExtractRunDataArray extractors; + ExtractorRunDatas extractors; #define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ do { \ if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ const MeshExtract *extractor = mesh_extract_override_get( \ &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ - mesh_extract_run_data_array_add(extractors, extractor); \ + extractors.append(extractor); \ } \ } while (0) @@ -847,9 +842,8 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, double rdata_start = PIL_check_seconds_timer(); #endif - eMRIterType iter_type; - eMRDataType data_flag; - extracts_flags_get(extractors, &iter_type, &data_flag); + eMRIterType iter_type = extractors.iter_types(); + eMRDataType data_flag = extractors.data_types(); MeshRenderData *mr = mesh_render_data_create(me, extraction_cache, @@ -880,11 +874,11 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, uint threads_to_use = 0; /* First run the requested extractors that do not support asynchronous ranges. */ - for (const MeshExtractRunData &run_data : extractors) { + for (const ExtractorRunData &run_data : extractors) { const MeshExtract *extractor = run_data.extractor; if (!extractor->use_threading) { - MeshExtractRunDataArray *single_threaded_extractors = new MeshExtractRunDataArray(); - mesh_extract_run_data_array_add(*single_threaded_extractors, extractor); + ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas(); + single_threaded_extractors->append(extractor); ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( mr, cache, single_threaded_extractors, mbc, NULL); struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, @@ -895,8 +889,8 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, } /* Distribute the remaining extractors into ranges per core. */ - MeshExtractRunDataArray *multi_threaded_extractors = new MeshExtractRunDataArray(); - mesh_extract_run_data_array_filter_threading(extractors, *multi_threaded_extractors); + ExtractorRunDatas *multi_threaded_extractors = new ExtractorRunDatas(); + extractors.filter_threaded_extractors_into(*multi_threaded_extractors); if (!multi_threaded_extractors->is_empty()) { /* * Determine the number of thread to use for multithreading. @@ -927,7 +921,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, } else { /* Run all requests on the same thread. */ - MeshExtractRunDataArray *extractors_copy = new MeshExtractRunDataArray(extractors); + ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors); ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( mr, cache, extractors_copy, mbc, NULL); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c index cc787b5a40f..324ebd2ec38 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -246,7 +246,7 @@ const MeshExtract extract_tris = {.init = extract_tris_init, .iter_looptri_bm = extract_tris_iter_looptri_bm, .iter_looptri_mesh = extract_tris_iter_looptri_mesh, .finish = extract_tris_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; @@ -375,7 +375,7 @@ const MeshExtract extract_lines = {.init = extract_lines_init, .iter_ledge_bm = extract_lines_iter_ledge_bm, .iter_ledge_mesh = extract_lines_iter_ledge_mesh, .finish = extract_lines_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; @@ -414,7 +414,7 @@ const MeshExtract extract_lines_with_lines_loose = { .iter_ledge_bm = extract_lines_iter_ledge_bm, .iter_ledge_mesh = extract_lines_iter_ledge_mesh, .finish = extract_lines_with_lines_loose_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; @@ -542,7 +542,7 @@ const MeshExtract extract_points = {.init = extract_points_init, .iter_lvert_bm = extract_points_iter_lvert_bm, .iter_lvert_mesh = extract_points_iter_lvert_mesh, .finish = extract_points_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; @@ -617,7 +617,7 @@ const MeshExtract extract_fdots = {.init = extract_fdots_init, .iter_poly_bm = extract_fdots_iter_poly_bm, .iter_poly_mesh = extract_fdots_iter_poly_mesh, .finish = extract_fdots_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; @@ -700,7 +700,7 @@ const MeshExtract extract_lines_paint_mask = { .init = extract_lines_paint_mask_init, .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh, .finish = extract_lines_paint_mask_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)}; @@ -852,7 +852,7 @@ const MeshExtract extract_lines_adjacency = { .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm, .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh, .finish = extract_lines_adjacency_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)}; @@ -928,7 +928,7 @@ const MeshExtract extract_edituv_tris = { .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm, .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh, .finish = extract_edituv_tris_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)}; @@ -1012,7 +1012,7 @@ const MeshExtract extract_edituv_lines = { .iter_poly_bm = extract_edituv_lines_iter_poly_bm, .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh, .finish = extract_edituv_lines_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)}; @@ -1091,7 +1091,7 @@ const MeshExtract extract_edituv_points = { .iter_poly_bm = extract_edituv_points_iter_poly_bm, .iter_poly_mesh = extract_edituv_points_iter_poly_mesh, .finish = extract_edituv_points_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)}; @@ -1182,7 +1182,7 @@ const MeshExtract extract_edituv_fdots = { .iter_poly_bm = extract_edituv_fdots_iter_poly_bm, .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh, .finish = extract_edituv_fdots_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)}; @@ -1362,7 +1362,7 @@ const MeshExtract extract_pos_nor = {.init = extract_pos_nor_init, .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, .finish = extract_pos_nor_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; @@ -1550,7 +1550,7 @@ const MeshExtract extract_pos_nor_hq = { .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm, .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh, .finish = extract_pos_nor_hq_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; @@ -1641,7 +1641,7 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, const MeshExtract extract_lnor_hq = {.init = extract_lnor_hq_init, .iter_poly_bm = extract_lnor_hq_iter_poly_bm, .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, + .data_type = MR_DATA_LOOP_NOR, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; @@ -1730,7 +1730,7 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, const MeshExtract extract_lnor = {.init = extract_lnor_init, .iter_poly_bm = extract_lnor_iter_poly_bm, .iter_poly_mesh = extract_lnor_iter_poly_mesh, - .data_flag = MR_DATA_LOOP_NOR, + .data_type = MR_DATA_LOOP_NOR, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; @@ -1825,7 +1825,7 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca } const MeshExtract extract_uv = {.init = extract_uv_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; @@ -2014,7 +2014,7 @@ static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *c } const MeshExtract extract_tan = {.init = extract_tan_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | + .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; @@ -2033,7 +2033,7 @@ static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache const MeshExtract extract_tan_hq = { .init = extract_tan_hq_init, - .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, .use_threading = false, }; @@ -2132,7 +2132,7 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr, const MeshExtract extract_sculpt_data = { .init = extract_sculpt_data_init, - .data_flag = 0, + .data_type = 0, /* TODO: enable threading. */ .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)}; @@ -2280,7 +2280,7 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * } const MeshExtract extract_vcol = {.init = extract_vcol_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; @@ -2366,7 +2366,7 @@ const MeshExtract extract_orco = {.init = extract_orco_init, .iter_poly_bm = extract_orco_iter_poly_bm, .iter_poly_mesh = extract_orco_iter_poly_mesh, .finish = extract_orco_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; @@ -2567,7 +2567,7 @@ const MeshExtract extract_edge_fac = { .iter_ledge_bm = extract_edge_fac_iter_ledge_bm, .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh, .finish = extract_edge_fac_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)}; @@ -2721,7 +2721,7 @@ const MeshExtract extract_weights = {.init = extract_weights_init, .iter_poly_bm = extract_weights_iter_poly_bm, .iter_poly_mesh = extract_weights_iter_poly_mesh, .finish = extract_weights_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; @@ -3000,7 +3000,7 @@ const MeshExtract extract_edit_data = { .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh, .iter_lvert_bm = extract_edit_data_iter_lvert_bm, .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)}; @@ -3111,7 +3111,7 @@ const MeshExtract extract_edituv_data = { .iter_poly_bm = extract_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_edituv_data_iter_poly_mesh, .finish = extract_edituv_data_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)}; @@ -3227,7 +3227,7 @@ static void extract_edituv_stretch_area_finish(const MeshRenderData *mr, const MeshExtract extract_edituv_stretch_area = { .init = extract_edituv_stretch_area_init, .finish = extract_edituv_stretch_area_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)}; @@ -3430,7 +3430,7 @@ const MeshExtract extract_edituv_stretch_angle = { .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm, .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh, .finish = extract_edituv_stretch_angle_finish, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)}; @@ -4037,7 +4037,7 @@ const MeshExtract extract_mesh_analysis = { .finish = extract_analysis_iter_finish_mesh, /* This is not needed for all visualization types. * * Maybe split into different extract. */ - .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, + .data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)}; @@ -4117,7 +4117,7 @@ const MeshExtract extract_fdots_pos = { .init = extract_fdots_pos_init, .iter_poly_bm = extract_fdots_pos_iter_poly_bm, .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)}; @@ -4199,7 +4199,7 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr, const MeshExtract extract_fdots_nor = { .init = extract_fdots_nor_init, .finish = extract_fdots_nor_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; @@ -4276,7 +4276,7 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr, const MeshExtract extract_fdots_nor_hq = { .init = extract_fdots_nor_hq_init, .finish = extract_fdots_nor_hq_finish, - .data_flag = MR_DATA_POLY_NOR, + .data_type = MR_DATA_POLY_NOR, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)}; @@ -4375,7 +4375,7 @@ const MeshExtract extract_fdots_uv = { .iter_poly_bm = extract_fdots_uv_iter_poly_bm, .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh, .finish = extract_fdots_uv_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)}; @@ -4447,7 +4447,7 @@ const MeshExtract extract_fdots_edituv_data = { .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm, .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh, .finish = extract_fdots_edituv_data_finish, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)}; @@ -4504,7 +4504,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr, const MeshExtract extract_skin_roots = { .init = extract_skin_roots_init, - .data_flag = 0, + .data_type = 0, .use_threading = false, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)}; @@ -4676,7 +4676,7 @@ const MeshExtract extract_poly_idx = { .init = extract_select_idx_init, .iter_poly_bm = extract_poly_idx_iter_poly_bm, .iter_poly_mesh = extract_poly_idx_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)}; @@ -4686,7 +4686,7 @@ const MeshExtract extract_edge_idx = { .iter_poly_mesh = extract_edge_idx_iter_poly_mesh, .iter_ledge_bm = extract_edge_idx_iter_ledge_bm, .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)}; @@ -4698,7 +4698,7 @@ const MeshExtract extract_vert_idx = { .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh, .iter_lvert_bm = extract_vert_idx_iter_lvert_bm, .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)}; @@ -4743,6 +4743,6 @@ const MeshExtract extract_fdot_idx = { .init = extract_fdot_idx_init, .iter_poly_bm = extract_fdot_idx_iter_poly_bm, .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, - .data_flag = 0, + .data_type = 0, .use_threading = true, .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; \ No newline at end of file diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index 6dd2ef4c16f..fbce3b17a40 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -430,7 +430,7 @@ typedef struct MeshExtract { /** Executed on one worker thread after all elements iterations. */ ExtractFinishFn *finish; /** Used to request common data. */ - const eMRDataType data_flag; + const eMRDataType data_type; /** Used to know if the element callbacks are thread-safe and can be parallelized. */ const bool use_threading; /** -- cgit v1.2.3 From 633b70905a3e8c75bf30e7a8eae447092f46b7c8 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 13:03:42 +0200 Subject: Cleanup: NULL->nullptr. --- source/blender/draw/intern/draw_cache_extract_mesh.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 5a086805580..aa588bbce0a 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -781,7 +781,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, * +-----> | extract_task2_loop_3 | * +----------------------+ */ - const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != NULL; + const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != nullptr; const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || GPU_use_hq_normals_workaround(); @@ -880,7 +880,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas(); single_threaded_extractors->append(extractor); ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( - mr, cache, single_threaded_extractors, mbc, NULL); + mr, cache, single_threaded_extractors, mbc, nullptr); struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); @@ -923,7 +923,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, /* Run all requests on the same thread. */ ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors); ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh( - mr, cache, extractors_copy, mbc, NULL); + mr, cache, extractors_copy, mbc, nullptr); struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); -- cgit v1.2.3 From 6f1af446953dd52495699d3043330d963c13df4f Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 13:14:39 +0200 Subject: Cleanup: remove unused parameter. --- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- source/blender/draw/intern/draw_cache_extract.h | 1 - source/blender/draw/intern/draw_cache_extract_mesh.cc | 4 ---- source/blender/draw/intern/draw_cache_extract_mesh_private.h | 1 - source/blender/draw/intern/draw_cache_extract_mesh_render_data.c | 1 - source/blender/draw/intern/draw_cache_impl_mesh.c | 3 --- source/tools | 2 +- 8 files changed, 3 insertions(+), 13 deletions(-) diff --git a/release/scripts/addons b/release/scripts/addons index 4fcdbfe7c20..27fe7f3a4f9 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 4fcdbfe7c20edfc1204c0aa46c98ea25354abcd9 +Subproject commit 27fe7f3a4f964b53af436c4da4ddea337eff0c7e diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 7d78c8a63f2..5a82baad9f9 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 7d78c8a63f2f4b146f9327ddc0d567a5921b94ea +Subproject commit 5a82baad9f986722104280e8354a4427d8e9eab1 diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 36756616ca7..810deaec349 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -299,7 +299,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, const Scene *scene, const ToolSettings *ts, const bool use_hide); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index aa588bbce0a..4b047ec51f8 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -746,7 +746,6 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -853,7 +852,6 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, obmat, do_final, do_uvedit, - cd_layer_used, ts, iter_type); mr->use_hide = use_hide; @@ -972,7 +970,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool do_final, const bool do_uvedit, const bool use_subsurf_fdots, - const DRW_MeshCDMask *cd_layer_used, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -989,7 +986,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, do_final, do_uvedit, use_subsurf_fdots, - cd_layer_used, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index fbce3b17a40..7640582645f 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -451,7 +451,6 @@ MeshRenderData *mesh_render_data_create(Mesh *me, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const DRW_MeshCDMask *cd_used, const ToolSettings *ts, const eMRIterType iter_type); void mesh_render_data_free(MeshRenderData *mr); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c index 56b31a36fe5..bec8159c6ad 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -238,7 +238,6 @@ MeshRenderData *mesh_render_data_create(Mesh *me, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const DRW_MeshCDMask *UNUSED(cd_used), const ToolSettings *ts, const eMRIterType iter_type) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 0d91432d4e5..3cc71e47f28 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -1569,7 +1569,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, false, true, false, - &cache->cd_used, scene, ts, true); @@ -1588,7 +1587,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, false, false, use_subsurf_fdots, - &cache->cd_used, scene, ts, true); @@ -1606,7 +1604,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, true, false, use_subsurf_fdots, - &cache->cd_used, scene, ts, use_hide); diff --git a/source/tools b/source/tools index f99d29ae3e6..01f51a0e551 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit f99d29ae3e6ad44d45d79309454c45f8088781a4 +Subproject commit 01f51a0e551ab730f0934dc6488613690ac4bf8f -- cgit v1.2.3 From c078540512722e465e8cfc555dd958827b378a11 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 1 Jun 2021 13:18:04 +0200 Subject: Cleanup: remove unused parameter. --- source/blender/draw/intern/draw_cache_extract_mesh.cc | 2 +- source/blender/draw/intern/draw_cache_extract_mesh_private.h | 1 - source/blender/draw/intern/draw_cache_extract_mesh_render_data.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 4b047ec51f8..622ea191a0a 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -563,7 +563,7 @@ static void mesh_extract_render_data_node_exec(void *__restrict task_data) const eMRIterType iter_type = update_task_data->iter_type; const eMRDataType data_flag = update_task_data->data_flag; - mesh_render_data_update_normals(mr, iter_type, data_flag); + mesh_render_data_update_normals(mr, data_flag); mesh_render_data_update_looptris(mr, iter_type, data_flag); } diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index 7640582645f..d3acf10b72c 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -455,7 +455,6 @@ MeshRenderData *mesh_render_data_create(Mesh *me, const eMRIterType iter_type); void mesh_render_data_free(MeshRenderData *mr); void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRIterType iter_type, const eMRDataType data_flag); void mesh_render_data_update_looptris(MeshRenderData *mr, const eMRIterType iter_type, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c index bec8159c6ad..db28e4f3ba3 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -151,7 +151,6 @@ void mesh_render_data_update_looptris(MeshRenderData *mr, } void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRIterType UNUSED(iter_type), const eMRDataType data_flag) { Mesh *me = mr->me; -- cgit v1.2.3 From 6899dcab5339d1f17dd2ea665a38e4ab28ab23fc Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Sun, 30 May 2021 22:26:31 +0200 Subject: Fix T88658: Force Fields of curve shape can crash if curve has only one point `bvhtree_from_mesh_edges_create_tree` can actually leave the BVHTree NULL (e.g. if no edges are present). Now dont allocate `BVHTreeFromMesh` on the `SurfaceModifierData` at all in case the tree would be NULL anyways. Places like `get_effector_data` check for `SurfaceModifierData`- >`BVHTreeFromMesh` and dont try to stuff like getting a closest point on surface, which would crash as soon as BVHNodes would need to be accessed (from the NULL BVHTree). Maniphest Tasks: T88658 Differential Revision: https://developer.blender.org/D11443 --- source/blender/modifiers/intern/MOD_surface.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index d2c011a21d3..bfd4cd81803 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -184,13 +184,17 @@ static void deformVerts(ModifierData *md, surmd->cfra = cfra; - surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh"); + const bool has_poly = surmd->mesh->totpoly > 0; + const bool has_edge = surmd->mesh->totedge > 0; + if (has_poly || has_edge) { + surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh"); - if (surmd->mesh->totpoly) { - BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2); - } - else { - BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2); + if (has_poly) { + BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2); + } + else if (has_edge) { + BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2); + } } } } -- cgit v1.2.3 From 6583fb67c6e1e5d714d723a09312e9dd25e02152 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 1 Jun 2021 15:24:29 +0200 Subject: Geometry Nodes: add empty material slot to new meshes This fixes T88455 by adding an empty material slot to newly generated meshes. This allows the object to overwrite the "default" material without any extra nodes. Technically, all polygons reference the material index 0 already, so it makes sense to add a material slot for this material index. Differential Revision: https://developer.blender.org/D11439 --- source/blender/blenkernel/BKE_material.h | 1 + source/blender/blenkernel/intern/material.c | 16 ++++++++++++++++ .../nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 2 ++ .../geometry/nodes/node_geo_mesh_primitive_circle.cc | 2 ++ .../nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc | 2 ++ .../nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc | 2 ++ .../geometry/nodes/node_geo_mesh_primitive_cylinder.cc | 2 ++ .../nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc | 2 ++ .../geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc | 2 ++ .../nodes/geometry/nodes/node_geo_mesh_primitive_line.cc | 2 ++ .../geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc | 2 ++ .../nodes/geometry/nodes/node_geo_volume_to_mesh.cc | 2 ++ 12 files changed, 37 insertions(+) diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index dc471fcb62f..69e2d52e1dd 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -113,6 +113,7 @@ void BKE_id_material_clear(struct Main *bmain, struct ID *id); struct Material *BKE_object_material_get_eval(struct Object *ob, short act); int BKE_object_material_count_eval(struct Object *ob); void BKE_id_material_eval_assign(struct ID *id, int slot, struct Material *material); +void BKE_id_material_eval_ensure_default_slot(struct ID *id); /* rendering */ diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 73b64e6efb3..557fe5f7e75 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -770,6 +770,7 @@ int BKE_object_material_count_eval(Object *ob) void BKE_id_material_eval_assign(ID *id, int slot, Material *material) { + BLI_assert(slot >= 1); Material ***materials_ptr = BKE_id_material_array_p(id); short *len_ptr = BKE_id_material_len_p(id); if (ELEM(NULL, materials_ptr, len_ptr)) { @@ -793,6 +794,21 @@ void BKE_id_material_eval_assign(ID *id, int slot, Material *material) (*materials_ptr)[slot_index] = material; } +/** + * Add an empty material slot if the id has no material slots. This material slot allows the + * material to be overwritten by object-linked materials. + */ +void BKE_id_material_eval_ensure_default_slot(ID *id) +{ + short *len_ptr = BKE_id_material_len_p(id); + if (len_ptr == NULL) { + return; + } + if (*len_ptr == 0) { + BKE_id_material_eval_assign(id, 1, NULL); + } +} + Material *BKE_gpencil_material(Object *ob, short act) { Material *ma = BKE_object_material_get(ob, act); 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 f0effdc71f6..8979949736c 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 @@ -21,6 +21,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_spline.hh" @@ -296,6 +297,7 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(), (profile_curve == nullptr) ? vert_curve : *profile_curve); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index aace71d1d40..f19d533d0b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -214,6 +215,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) } Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type); + BKE_id_material_eval_ensure_default_slot(&mesh->id); BLI_assert(BKE_mesh_is_valid(mesh)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 2806472286e..4c1521aa6f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -561,6 +562,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) Mesh *mesh = create_cylinder_or_cone_mesh( radius_top, radius_bottom, depth, verts_num, fill_type); + BKE_id_material_eval_ensure_default_slot(&mesh->id); /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); 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 1d31068653e..b34913df843 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 @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "BKE_lib_id.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "bmesh.h" @@ -64,6 +65,7 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) const float size = params.extract_input("Size"); Mesh *mesh = create_cube_mesh(size); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index f443b4387d3..c7b9fb920f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -75,6 +76,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 5a4bab86421..ac2f5a23a4d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -167,6 +168,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y); BLI_assert(BKE_mesh_is_valid(mesh)); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } 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 242cc6ed7df..5f7d8150022 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 @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "BKE_lib_id.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "bmesh.h" @@ -67,6 +68,7 @@ static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input("Radius"); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 2eeb87695a8..d93c4e39fda 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -165,6 +166,7 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) const int count = params.extract_input("Count"); mesh = create_line_mesh(start, delta, count); } + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index cc93e71a5dd..7d340679269 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -296,6 +297,7 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input("Radius"); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index c6049787970..403f4906d07 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -23,6 +23,7 @@ #include "node_geometry_util.hh" #include "BKE_lib_id.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_volume.h" @@ -134,6 +135,7 @@ static void create_mesh_from_volume(GeometrySet &geometry_set_in, if (mesh == nullptr) { return; } + BKE_id_material_eval_ensure_default_slot(&mesh->id); MeshComponent &dst_component = geometry_set_out.get_component_for_write(); dst_component.replace(mesh); } -- cgit v1.2.3 From 404b946ac0e4f86e99471c2805570dfe3a8cc67e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 1 Jun 2021 17:36:33 +0200 Subject: LibOverride: Fix again infinite loop in resync in some complex/degenerated cases. Broken in recent refactor of (recursive)resync, reported by studio, thanks. --- source/blender/blenkernel/intern/lib_override.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 4d5085d6ad5..9a2887a1d16 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1473,16 +1473,16 @@ static void lib_override_library_main_resync_on_library_indirect_level( } int level = 0; - id = lib_override_library_main_resync_find_root_recurse(id, &level); - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); - do_continue = true; - /* In complex non-supported cases, with several different override hierarchies sharing * relations between each-other, we may end up not actually updating/replacing the given * root id (see e.g. pro/shots/110_rextoria/110_0150_A/110_0150_A.anim.blend of sprites * project repository, r2687). * This can lead to infinite loop here, at least avoid this. */ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + id = lib_override_library_main_resync_find_root_recurse(id, &level); + id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); + do_continue = true; CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, id->lib); const bool success = BKE_lib_override_library_resync( -- cgit v1.2.3 From 5b6e0bad1bbc33faba2247602349942ce163b7df Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 1 Jun 2021 11:57:30 +0200 Subject: Fix T88715: particle size influence texture not working for 'keyed' or 'none' physics types This was reported for the special case of mapping with "Strand / Particle" coords, but was not working with other coordinates either. Dont see a reason for not supporting Size influence textures for these kinds of particles (and since these types of particles have an "age" like all others as well, even the "Strand / Particle" coords are supported here as well) Maniphest Tasks: T88715 Differential Revision: https://developer.blender.org/D11449 --- source/blender/blenkernel/intern/particle_system.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index c727a144c87..ce4be411c9a 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -4912,9 +4912,12 @@ void particle_system_update(struct Depsgraph *depsgraph, sim.psmd->flag |= eParticleSystemFlag_Pars; } + ParticleTexture ptex; + LOOP_EXISTING_PARTICLES { - pa->size = part->size; + psys_get_texture(&sim, pa, &ptex, PAMAP_SIZE, cfra); + pa->size = part->size * ptex.size; if (part->randsize > 0.0f) { pa->size *= 1.0f - part->randsize * psys_frand(psys, p + 1); } -- cgit v1.2.3 From 17b09b509c069dad6817e4c123bca50bd0682578 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 1 Jun 2021 12:03:59 -0400 Subject: Geometry Nodes: Skip calculating normals in transform node This commit skips the eager recalculation of mesh normals in the transform node. Often another deformation or topology-altering operation will happen after the transform node, which means the recalculation was redundant anyway. In one of my test cases this made the node more than 14x faster. Though depending on the situation the cost of updating the normals may just be shifted elsewhere. --- source/blender/nodes/geometry/nodes/node_geo_transform.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 9714a4f8a80..1ad5dbea492 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -20,6 +20,7 @@ #include "BLI_float4x4.hh" +#include "DNA_mesh_types.h" #include "DNA_pointcloud_types.h" #include "DNA_volume_types.h" @@ -72,7 +73,8 @@ void transform_mesh(Mesh *mesh, else { const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); BKE_mesh_transform(mesh, matrix.values, false); - BKE_mesh_calc_normals(mesh); + mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; } } @@ -158,7 +160,6 @@ static void transform_curve(CurveEval &curve, const float3 rotation, const float3 scale) { - if (use_translate(rotation, scale)) { curve.translate(translation); } -- cgit v1.2.3 From 3400ba329e6b9ce865bcc592889edb7e958b3b37 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 1 Jun 2021 23:06:31 +0200 Subject: VSE: Use own category for metadata panel Metadata panel was visible in each category. In other editors, this panel is usually placed in category with other source media properties. In sequencer, metadata is transfered over while compositing and relation to particular strip is lost, therefore separate category for metadata seems to be best option. Since Metadata panel is alone in this category, it will be open by default. --- source/blender/editors/space_sequencer/sequencer_buttons.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index 11614d94862..1e0ecfd890e 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -111,10 +111,10 @@ void sequencer_buttons_register(ARegionType *art) pt = MEM_callocN(sizeof(PanelType), "spacetype sequencer panel metadata"); strcpy(pt->idname, "SEQUENCER_PT_metadata"); strcpy(pt->label, N_("Metadata")); + strcpy(pt->category, "Metadata"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->poll = metadata_panel_context_poll; pt->draw = metadata_panel_context_draw; - pt->flag |= PANEL_TYPE_DEFAULT_CLOSED; pt->order = 10; BLI_addtail(&art->paneltypes, pt); } -- cgit v1.2.3 From 464797078d44a55b45560f561b20a546ce46bcd7 Mon Sep 17 00:00:00 2001 From: Wannes Malfait Date: Tue, 1 Jun 2021 17:32:03 -0400 Subject: Geometry Nodes: Add Delete Geometry Node This node is similar to the mask modifier, but it deletes the elements of the geometry corresponding to the selection, which is retrieved as a boolean attribute. The node currently supports both mesh and point cloud data. For meshes, which elements are deleted depends on the domain of the input selection attribute, just like how behavior depends on the selection mode in mesh edit mode. In the future this node will support curve data, and ideally volume data in some way. Differential Revision: https://developer.blender.org/D10748 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/modifiers/intern/MOD_mask.cc | 37 +- source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../blender/nodes/geometry/node_geometry_util.hh | 8 + .../geometry/nodes/node_geo_delete_geometry.cc | 524 +++++++++++++++++++++ .../geometry/nodes/node_geo_point_separate.cc | 10 +- 10 files changed, 567 insertions(+), 18 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 05c7ef756c7..d4c21bf5a23 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -507,6 +507,7 @@ geometry_node_categories = [ ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ NodeItem("GeometryNodeBoundBox"), + NodeItem("GeometryNodeDeleteGeometry"), NodeItem("GeometryNodeTransform"), NodeItem("GeometryNodeJoinGeometry"), ]), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index fb6647cb68d..00111910d66 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1430,6 +1430,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #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 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index d0864e85373..0923917aa55 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5052,6 +5052,7 @@ static void registerGeometryNodes() register_node_type_geo_collection_info(); register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_resample(); + register_node_type_geo_delete_geometry(); register_node_type_geo_edge_split(); register_node_type_geo_input_material(); register_node_type_geo_is_viewport(); diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 812bfe3b375..a77f6cfe8ba 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -69,6 +69,19 @@ using blender::MutableSpan; using blender::Span; using blender::Vector; +/* For delete geometry node. */ +void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span vertex_map); +void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span vertex_map, + Span edge_map); +void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span vertex_map, + Span edge_map, + Span masked_poly_indices, + Span new_loop_starts); + static void initData(ModifierData *md) { MaskModifierData *mmd = (MaskModifierData *)md; @@ -237,9 +250,7 @@ static void computed_masked_polygons(const Mesh *mesh, *r_num_masked_loops = num_masked_loops; } -static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span vertex_map) +void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span vertex_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); for (const int i_src : vertex_map.index_range()) { @@ -256,10 +267,10 @@ static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, } } -static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span vertex_map, - Span edge_map) +void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span vertex_map, + Span edge_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); BLI_assert(src_mesh.totedge == edge_map.size()); @@ -279,12 +290,12 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, } } -static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span vertex_map, - Span edge_map, - Span masked_poly_indices, - Span new_loop_starts) +void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span vertex_map, + Span edge_map, + Span masked_poly_indices, + Span new_loop_starts) { for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 24085b31fc3..6ecf46647a0 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -164,6 +164,7 @@ set(SRC geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_resample.cc + 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_is_viewport.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index eadfed26be1..30b039bca40 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -52,6 +52,7 @@ void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_resample(void); +void register_node_type_geo_delete_geometry(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_input_material(void); void register_node_type_geo_is_viewport(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index ef5f25e7b57..081525ffb5b 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -291,6 +291,7 @@ DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bound DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") +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_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 81092798ca1..79a98c5ebf0 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -62,4 +62,12 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, Mesh *create_cube_mesh(const float size); +/** + * Copies the point domain attributes from `in_component` that are in the mask to `out_component`. + */ +void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, + GeometryComponent &result_component, + Span masks, + const bool invert); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc new file mode 100644 index 00000000000..9044081de90 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -0,0 +1,524 @@ +/* + * 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_array.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" + +#include "node_geometry_util.hh" + +/* Code from the mask modifier in MOD_mask.cc. */ +extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span vertex_map); +extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span vertex_map, + blender::Span edge_map); +extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span vertex_map, + blender::Span edge_map, + blender::Span masked_poly_indices, + blender::Span new_loop_starts); + +static bNodeSocketTemplate geo_node_delete_geometry_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Selection")}, + {SOCK_BOOLEAN, N_("Invert")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_delete_geometry_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void delete_point_cloud_selection(const PointCloudComponent &in_component, + const GeoNodeExecParams ¶ms, + PointCloudComponent &out_component) +{ + const bool invert = params.get_input("Invert"); + const std::string selection_name = params.get_input("Selection"); + if (selection_name.empty()) { + return; + } + + const GVArray_Typed selection_attribute = in_component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_POINT, false); + VArray_Span selection{selection_attribute}; + + const int total = selection.count(invert); + if (total == 0) { + out_component.clear(); + return; + } + out_component.replace(BKE_pointcloud_new_nomain(total)); + + /* Invert the inversion, because this deletes the selected points instead of keeping them. */ + copy_point_attributes_based_on_mask(in_component, out_component, selection, !invert); +} + +static void compute_selected_vertices_from_vertex_selection(const VArray &vertex_selection, + const bool invert, + MutableSpan r_vertex_map, + uint *r_num_selected_vertices) +{ + BLI_assert(vertex_selection.size() == r_vertex_map.size()); + + uint num_selected_vertices = 0; + for (const int i : r_vertex_map.index_range()) { + if (vertex_selection[i] != invert) { + r_vertex_map[i] = num_selected_vertices; + num_selected_vertices++; + } + else { + r_vertex_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; +} + +static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, + const VArray &vertex_selection, + const bool invert, + MutableSpan r_edge_map, + uint *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == r_edge_map.size()); + + uint num_selected_edges = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + + /* Only add the edge if both vertices will be in the new mesh. */ + if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_edges = num_selected_edges; +} + +static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, + const VArray &vertex_selection, + const bool invert, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + BLI_assert(mesh.totvert == vertex_selection.size()); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_verts_in_selection = true; + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (vertex_selection[loop.v] == invert) { + all_verts_in_selection = false; + break; + } + } + + if (all_verts_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the edge + * are kept along with the edge. + */ +static void compute_selected_vertices_and_edges_from_edge_selection( + const Mesh &mesh, + const VArray &edge_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + uint *r_num_selected_vertices, + uint *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == edge_selection.size()); + + uint num_selected_edges = 0; + uint num_selected_vertices = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + if (edge_selection[i] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + if (r_vertex_map[edge.v1] == -1) { + r_vertex_map[edge.v1] = num_selected_vertices; + num_selected_vertices++; + } + if (r_vertex_map[edge.v2] == -1) { + r_vertex_map[edge.v2] = num_selected_vertices; + num_selected_vertices++; + } + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; +} + +/** + * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that + * polygon is kept. + */ +static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, + const VArray &edge_selection, + const bool invert, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_edges_in_selection = true; + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (edge_selection[loop.e] == invert) { + all_edges_in_selection = false; + break; + } + } + + if (all_edges_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all + * vertices of that polygon or edge are in the selection. + */ +static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, + const VArray &vertex_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + compute_selected_vertices_from_vertex_selection( + vertex_selection, invert, r_vertex_map, r_num_selected_vertices); + + compute_selected_edges_from_vertex_selection( + mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + + compute_selected_polygons_from_vertex_selection(mesh, + vertex_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to + * that edge are kept as well. The polygons are kept if all edges are in the selection. + */ +static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, + const VArray &edge_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + r_vertex_map.fill(-1); + compute_selected_vertices_and_edges_from_edge_selection(mesh, + edge_selection, + invert, + r_vertex_map, + r_edge_map, + r_num_selected_vertices, + r_num_selected_edges); + compute_selected_polygons_from_edge_selection(mesh, + edge_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices + * belonging to that polygon are kept as well. + */ +static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, + const VArray &poly_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + BLI_assert(mesh.totedge == r_edge_map.size()); + r_vertex_map.fill(-1); + r_edge_map.fill(-1); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + uint num_selected_vertices = 0; + uint num_selected_edges = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + + /* Add the vertices and the edges. */ + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + /* Check first if it has not yet been added. */ + if (r_vertex_map[loop.v] == -1) { + r_vertex_map[loop.v] = num_selected_vertices; + num_selected_vertices++; + } + if (r_edge_map[loop.e] == -1) { + r_edge_map[loop.e] = num_selected_edges; + num_selected_edges++; + } + } + } + } + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +using FillMapsFunction = void (*)(const Mesh &mesh, + const VArray &selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops); + +/** + * Delete the parts of the mesh that are in the selection. The `fill_maps_function` + * depends on the selection type: vertices, edges or faces. + */ +static Mesh *delete_mesh_selection(const Mesh &mesh_in, + const VArray &selection, + const bool invert, + FillMapsFunction fill_maps_function) +{ + Array vertex_map(mesh_in.totvert); + uint num_selected_vertices; + + Array edge_map(mesh_in.totedge); + uint num_selected_edges; + + Vector selected_poly_indices; + Vector new_loop_starts; + uint num_selected_polys; + uint num_selected_loops; + + /* Fill all the maps based on the selection. We delete everything + * in the selection instead of keeping it, so we need to invert it. */ + fill_maps_function(mesh_in, + selection, + !invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + + Mesh *result = BKE_mesh_new_nomain_from_template(&mesh_in, + num_selected_vertices, + num_selected_edges, + 0, + num_selected_loops, + num_selected_polys); + + /* Copy the selected parts of the mesh over to the new mesh. */ + copy_masked_vertices_to_new_mesh(mesh_in, *result, vertex_map); + copy_masked_edges_to_new_mesh(mesh_in, *result, vertex_map, edge_map); + copy_masked_polys_to_new_mesh( + 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; + + return result; +} + +static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name) +{ + std::optional selection_attribute = component.attribute_get_meta_data(name); + if (!selection_attribute) { + /* The node will not do anything in this case, but this function must return something. */ + return ATTR_DOMAIN_POINT; + } + + /* Corners can't be deleted separately, so interpolate corner attributes + * to the face domain. Note that this choice is somewhat arbitrary. */ + if (selection_attribute->domain == ATTR_DOMAIN_CORNER) { + return ATTR_DOMAIN_FACE; + } + + return selection_attribute->domain; +} + +static void delete_mesh_selection(MeshComponent &component, + const GeoNodeExecParams ¶ms, + const Mesh &mesh_in) +{ + const bool invert_selection = params.get_input("Invert"); + const std::string selection_name = params.get_input("Selection"); + if (selection_name.empty()) { + return; + } + /* Figure out the best domain to use. */ + const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name); + + /* This already checks if the attribute exists, and displays a warning in that case. */ + GVArray_Typed selection = params.get_input_attribute( + "Selection", component, selection_domain, false); + + /* Check if there is anything to delete. */ + bool delete_nothing = true; + for (const int i : selection.index_range()) { + if (selection[i] != invert_selection) { + delete_nothing = false; + break; + } + } + if (delete_nothing) { + return; + } + + Mesh *mesh_out; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert_selection, compute_selected_mesh_data_from_vertex_selection); + break; + case ATTR_DOMAIN_EDGE: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert_selection, compute_selected_mesh_data_from_edge_selection); + break; + case ATTR_DOMAIN_FACE: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert_selection, compute_selected_mesh_data_from_poly_selection); + break; + default: + BLI_assert_unreachable(); + break; + } + component.replace_mesh_but_keep_vertex_group_names(mesh_out); +} + +static void geo_node_delete_geometry_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + GeometrySet out_set(geometry_set); + if (geometry_set.has()) { + delete_point_cloud_selection(*geometry_set.get_component_for_read(), + params, + out_set.get_component_for_write()); + } + if (geometry_set.has()) { + delete_mesh_selection(out_set.get_component_for_write(), + params, + *geometry_set.get_mesh_for_read()); + } + params.set_output("Geometry", std::move(out_set)); +} + +} // namespace blender::nodes + +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); + node_type_socket_templates(&ntype, geo_node_delete_geometry_in, geo_node_delete_geometry_out); + ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; + nodeRegisterType(&ntype); +} 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 312ca5b8c33..fc04d1e275f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -52,10 +52,10 @@ static void copy_data_based_on_mask(Span data, } } -static void copy_attributes_based_on_mask(const GeometryComponent &in_component, - GeometryComponent &result_component, - Span masks, - const bool invert) +void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, + GeometryComponent &result_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); @@ -118,7 +118,7 @@ static void separate_points_from_component(const GeometryComponent &in_component create_component_points(out_component, total); - copy_attributes_based_on_mask(in_component, out_component, masks, invert); + copy_point_attributes_based_on_mask(in_component, out_component, masks, invert); } static GeometrySet separate_geometry_set(const GeometrySet &set_in, -- cgit v1.2.3 From 6b5bbd22d9f8da025697f9078cf98a07e4e5ac99 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 1 Jun 2021 17:51:21 -0400 Subject: Cleanup: Avoid duplicating node input retrieval Pass the selection name and the invert argument to each component instead of retrieving them every time. --- .../geometry/nodes/node_geo_delete_geometry.cc | 51 +++++++++++----------- 1 file changed, 26 insertions(+), 25 deletions(-) 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 9044081de90..4769126fa3c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -22,6 +22,7 @@ #include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_pointcloud.h" +#include "BKE_spline.hh" #include "node_geometry_util.hh" @@ -55,15 +56,10 @@ static bNodeSocketTemplate geo_node_delete_geometry_out[] = { namespace blender::nodes { static void delete_point_cloud_selection(const PointCloudComponent &in_component, - const GeoNodeExecParams ¶ms, - PointCloudComponent &out_component) + PointCloudComponent &out_component, + const StringRef selection_name, + const bool invert) { - const bool invert = params.get_input("Invert"); - const std::string selection_name = params.get_input("Selection"); - if (selection_name.empty()) { - return; - } - const GVArray_Typed selection_attribute = in_component.attribute_get_for_read( selection_name, ATTR_DOMAIN_POINT, false); VArray_Span selection{selection_attribute}; @@ -444,25 +440,21 @@ static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const } static void delete_mesh_selection(MeshComponent &component, - const GeoNodeExecParams ¶ms, - const Mesh &mesh_in) + const Mesh &mesh_in, + const StringRef selection_name, + const bool invert) { - const bool invert_selection = params.get_input("Invert"); - const std::string selection_name = params.get_input("Selection"); - if (selection_name.empty()) { - return; - } /* Figure out the best domain to use. */ const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name); /* This already checks if the attribute exists, and displays a warning in that case. */ - GVArray_Typed selection = params.get_input_attribute( - "Selection", component, selection_domain, false); + GVArray_Typed selection = component.attribute_get_for_read( + selection_name, selection_domain, false); /* Check if there is anything to delete. */ bool delete_nothing = true; for (const int i : selection.index_range()) { - if (selection[i] != invert_selection) { + if (selection[i] != invert) { delete_nothing = false; break; } @@ -475,15 +467,15 @@ static void delete_mesh_selection(MeshComponent &component, switch (selection_domain) { case ATTR_DOMAIN_POINT: mesh_out = delete_mesh_selection( - mesh_in, selection, invert_selection, compute_selected_mesh_data_from_vertex_selection); + mesh_in, selection, invert, compute_selected_mesh_data_from_vertex_selection); break; case ATTR_DOMAIN_EDGE: mesh_out = delete_mesh_selection( - mesh_in, selection, invert_selection, compute_selected_mesh_data_from_edge_selection); + mesh_in, selection, invert, compute_selected_mesh_data_from_edge_selection); break; case ATTR_DOMAIN_FACE: mesh_out = delete_mesh_selection( - mesh_in, selection, invert_selection, compute_selected_mesh_data_from_poly_selection); + mesh_in, selection, invert, compute_selected_mesh_data_from_poly_selection); break; default: BLI_assert_unreachable(); @@ -497,17 +489,26 @@ static void geo_node_delete_geometry_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Geometry"); geometry_set = bke::geometry_set_realize_instances(geometry_set); + const bool invert = params.extract_input("Invert"); + const std::string selection_name = params.extract_input("Selection"); + if (selection_name.empty()) { + return; + } + GeometrySet out_set(geometry_set); if (geometry_set.has()) { delete_point_cloud_selection(*geometry_set.get_component_for_read(), - params, - out_set.get_component_for_write()); + out_set.get_component_for_write(), + selection_name, + invert); } if (geometry_set.has()) { delete_mesh_selection(out_set.get_component_for_write(), - params, - *geometry_set.get_mesh_for_read()); + *geometry_set.get_mesh_for_read(), + selection_name, + invert); } + params.set_output("Geometry", std::move(out_set)); } -- cgit v1.2.3 From 711ddea60e3d0985d6d7c44024debb72fbae985d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 1 Jun 2021 23:50:23 -0400 Subject: Fix assert with geometry node output The previous commit (my own) returned early without providing a value for the node's output geometry set, which is required. --- source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc | 1 + 1 file changed, 1 insertion(+) 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 4769126fa3c..4692cef6616 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -492,6 +492,7 @@ static void geo_node_delete_geometry_exec(GeoNodeExecParams params) const bool invert = params.extract_input("Invert"); const std::string selection_name = params.extract_input("Selection"); if (selection_name.empty()) { + params.set_output("Geometry", std::move(geometry_set)); return; } -- cgit v1.2.3 From b5a883fef980208298fbcaaca04732b58458b3b8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 2 Jun 2021 17:04:21 +1000 Subject: Cleanup: spelling in comments --- source/blender/blenkernel/intern/lib_id.c | 2 +- source/blender/compositor/intern/COM_NodeOperation.h | 4 ++-- source/blender/draw/engines/overlay/overlay_edit_mesh.c | 4 ++-- .../draw/intern/draw_cache_extract_mesh_extractors.c | 16 ++++++++-------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 490abe05169..f26b85338f0 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1714,7 +1714,7 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const boo bool result = false; char name[MAX_ID_NAME - 2]; - /* If library, don't rename (unless explicitely required), but do ensure proper sorting. */ + /* If library, don't rename (unless explicitly required), but do ensure proper sorting. */ if (!do_linked_data && ID_IS_LINKED(id)) { id_sort_by_name(lb, id, NULL); diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index 01068c7f812..11b293a400f 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -192,8 +192,8 @@ struct NodeOperationFlags { bool open_cl : 1; /** - * TODO: Remove this flag and SingleThreadedOperation if tiled implemention is removed. - * Full-frame implemention doesn't need it. + * TODO: Remove this flag and #SingleThreadedOperation if tiled implementation is removed. + * Full-frame implementation doesn't need it. */ bool single_threaded : 1; diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c index 40a9dbe01c0..7639911286f 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -149,7 +149,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) DRWState state_common = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA; /* Faces */ - /* Cage geom needs to be offsetted to avoid Z-fighting. */ + /* Cage geom needs an offset applied to avoid Z-fighting. */ for (int j = 0; j < 2; j++) { DRWPass **edit_face_ps = (j == 0) ? &psl->edit_mesh_faces_ps[i] : &psl->edit_mesh_faces_cage_ps[i]; @@ -197,7 +197,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) grp = pd->edit_mesh_skin_roots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); } - /* Facedots */ + /* Face-dots */ if (select_face && show_face_dots) { sh = OVERLAY_shader_edit_mesh_facedot(); grp = pd->edit_mesh_facedots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c index 324ebd2ec38..489974a79da 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -549,7 +549,7 @@ const MeshExtract extract_points = {.init = extract_points_init, /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Indices +/** \name Extract Face-dots Indices * \{ */ static void *extract_fdots_init(const MeshRenderData *mr, @@ -1098,7 +1098,7 @@ const MeshExtract extract_edituv_points = { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Edit UV Facedots Indices +/** \name Extract Edit UV Face-dots Indices * \{ */ static void *extract_edituv_fdots_init(const MeshRenderData *mr, @@ -4044,7 +4044,7 @@ const MeshExtract extract_mesh_analysis = { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots positions +/** \name Extract Face-dots positions * \{ */ static void *extract_fdots_pos_init(const MeshRenderData *mr, @@ -4124,7 +4124,7 @@ const MeshExtract extract_fdots_pos = { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Normal and edit flag +/** \name Extract Face-dots Normal and edit flag * \{ */ #define NOR_AND_FLAG_DEFAULT 0 #define NOR_AND_FLAG_SELECT 1 @@ -4206,7 +4206,7 @@ const MeshExtract extract_fdots_nor = { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots High Quality Normal and edit flag +/** \name Extract Face-dots High Quality Normal and edit flag * \{ */ static void *extract_fdots_nor_hq_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), @@ -4283,7 +4283,7 @@ const MeshExtract extract_fdots_nor_hq = { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots UV +/** \name Extract Face-dots UV * \{ */ typedef struct MeshExtract_FdotUV_Data { @@ -4382,7 +4382,7 @@ const MeshExtract extract_fdots_uv = { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Facedots Edit UV flag +/** \name Extract Face-dots Edit UV flag * \{ */ typedef struct MeshExtract_EditUVFdotData_Data { @@ -4745,4 +4745,4 @@ const MeshExtract extract_fdot_idx = { .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh, .data_type = 0, .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; \ No newline at end of file + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)}; -- cgit v1.2.3 From 3b3742c75fb0960997cb28742c0879412004358d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 2 Jun 2021 17:06:12 +1000 Subject: Cleanup: trailing commas to avoid right shift This matches most declarations already in this file. --- .../intern/draw_cache_extract_mesh_extractors.c | 191 ++++++++++++--------- 1 file changed, 107 insertions(+), 84 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c index 489974a79da..a806632c18d 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -242,13 +242,15 @@ static void extract_tris_finish(const MeshRenderData *mr, MEM_freeN(data); } -const MeshExtract extract_tris = {.init = extract_tris_init, - .iter_looptri_bm = extract_tris_iter_looptri_bm, - .iter_looptri_mesh = extract_tris_iter_looptri_mesh, - .finish = extract_tris_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris)}; +const MeshExtract extract_tris = { + .init = extract_tris_init, + .iter_looptri_bm = extract_tris_iter_looptri_bm, + .iter_looptri_mesh = extract_tris_iter_looptri_mesh, + .finish = extract_tris_finish, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris), +}; /** \} */ @@ -369,15 +371,17 @@ static void extract_lines_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(elb); } -const MeshExtract extract_lines = {.init = extract_lines_init, - .iter_poly_bm = extract_lines_iter_poly_bm, - .iter_poly_mesh = extract_lines_iter_poly_mesh, - .iter_ledge_bm = extract_lines_iter_ledge_bm, - .iter_ledge_mesh = extract_lines_iter_ledge_mesh, - .finish = extract_lines_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)}; +const MeshExtract extract_lines = { + .init = extract_lines_init, + .iter_poly_bm = extract_lines_iter_poly_bm, + .iter_poly_mesh = extract_lines_iter_poly_mesh, + .iter_ledge_bm = extract_lines_iter_ledge_bm, + .iter_ledge_mesh = extract_lines_iter_ledge_mesh, + .finish = extract_lines_finish, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines), +}; /** \} */ @@ -534,17 +538,19 @@ static void extract_points_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(elb); } -const MeshExtract extract_points = {.init = extract_points_init, - .iter_poly_bm = extract_points_iter_poly_bm, - .iter_poly_mesh = extract_points_iter_poly_mesh, - .iter_ledge_bm = extract_points_iter_ledge_bm, - .iter_ledge_mesh = extract_points_iter_ledge_mesh, - .iter_lvert_bm = extract_points_iter_lvert_bm, - .iter_lvert_mesh = extract_points_iter_lvert_mesh, - .finish = extract_points_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points)}; +const MeshExtract extract_points = { + .init = extract_points_init, + .iter_poly_bm = extract_points_iter_poly_bm, + .iter_poly_mesh = extract_points_iter_poly_mesh, + .iter_ledge_bm = extract_points_iter_ledge_bm, + .iter_ledge_mesh = extract_points_iter_ledge_mesh, + .iter_lvert_bm = extract_points_iter_lvert_bm, + .iter_lvert_mesh = extract_points_iter_lvert_mesh, + .finish = extract_points_finish, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points), +}; /** \} */ @@ -613,13 +619,15 @@ static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(elb); } -const MeshExtract extract_fdots = {.init = extract_fdots_init, - .iter_poly_bm = extract_fdots_iter_poly_bm, - .iter_poly_mesh = extract_fdots_iter_poly_mesh, - .finish = extract_fdots_finish, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots)}; +const MeshExtract extract_fdots = { + .init = extract_fdots_init, + .iter_poly_bm = extract_fdots_iter_poly_bm, + .iter_poly_mesh = extract_fdots_iter_poly_mesh, + .finish = extract_fdots_finish, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots), +}; /** \} */ @@ -1354,17 +1362,19 @@ static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -const MeshExtract extract_pos_nor = {.init = extract_pos_nor_init, - .iter_poly_bm = extract_pos_nor_iter_poly_bm, - .iter_poly_mesh = extract_pos_nor_iter_poly_mesh, - .iter_ledge_bm = extract_pos_nor_iter_ledge_bm, - .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh, - .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, - .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, - .finish = extract_pos_nor_finish, - .data_type = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)}; +const MeshExtract extract_pos_nor = { + .init = extract_pos_nor_init, + .iter_poly_bm = extract_pos_nor_iter_poly_bm, + .iter_poly_mesh = extract_pos_nor_iter_poly_mesh, + .iter_ledge_bm = extract_pos_nor_iter_ledge_bm, + .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh, + .iter_lvert_bm = extract_pos_nor_iter_lvert_bm, + .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh, + .finish = extract_pos_nor_finish, + .data_type = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor), +}; /** \} */ @@ -1638,12 +1648,14 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, } } -const MeshExtract extract_lnor_hq = {.init = extract_lnor_hq_init, - .iter_poly_bm = extract_lnor_hq_iter_poly_bm, - .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, - .data_type = MR_DATA_LOOP_NOR, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; +const MeshExtract extract_lnor_hq = { + .init = extract_lnor_hq_init, + .iter_poly_bm = extract_lnor_hq_iter_poly_bm, + .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh, + .data_type = MR_DATA_LOOP_NOR, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor), +}; /** \} */ /* ---------------------------------------------------------------------- */ @@ -1727,12 +1739,14 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, } } -const MeshExtract extract_lnor = {.init = extract_lnor_init, - .iter_poly_bm = extract_lnor_iter_poly_bm, - .iter_poly_mesh = extract_lnor_iter_poly_mesh, - .data_type = MR_DATA_LOOP_NOR, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor)}; +const MeshExtract extract_lnor = { + .init = extract_lnor_init, + .iter_poly_bm = extract_lnor_iter_poly_bm, + .iter_poly_mesh = extract_lnor_iter_poly_mesh, + .data_type = MR_DATA_LOOP_NOR, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor), +}; /** \} */ @@ -1824,10 +1838,12 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca return NULL; } -const MeshExtract extract_uv = {.init = extract_uv_init, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv)}; +const MeshExtract extract_uv = { + .init = extract_uv_init, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv), +}; /** \} */ @@ -2013,11 +2029,12 @@ static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *c return NULL; } -const MeshExtract extract_tan = {.init = extract_tan_init, - .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | - MR_DATA_LOOPTRI, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan)}; +const MeshExtract extract_tan = { + .init = extract_tan_init, + .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan), +}; /** \} */ @@ -2279,10 +2296,12 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache * return NULL; } -const MeshExtract extract_vcol = {.init = extract_vcol_init, - .data_type = 0, - .use_threading = false, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol)}; +const MeshExtract extract_vcol = { + .init = extract_vcol_init, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol), +}; /** \} */ @@ -2362,13 +2381,15 @@ static void extract_orco_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -const MeshExtract extract_orco = {.init = extract_orco_init, - .iter_poly_bm = extract_orco_iter_poly_bm, - .iter_poly_mesh = extract_orco_iter_poly_mesh, - .finish = extract_orco_finish, - .data_type = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco)}; +const MeshExtract extract_orco = { + .init = extract_orco_init, + .iter_poly_bm = extract_orco_iter_poly_bm, + .iter_poly_mesh = extract_orco_iter_poly_mesh, + .finish = extract_orco_finish, + .data_type = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco), +}; /** \} */ @@ -2717,13 +2738,15 @@ static void extract_weights_finish(const MeshRenderData *UNUSED(mr), MEM_freeN(data); } -const MeshExtract extract_weights = {.init = extract_weights_init, - .iter_poly_bm = extract_weights_iter_poly_bm, - .iter_poly_mesh = extract_weights_iter_poly_mesh, - .finish = extract_weights_finish, - .data_type = 0, - .use_threading = true, - .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights)}; +const MeshExtract extract_weights = { + .init = extract_weights_init, + .iter_poly_bm = extract_weights_iter_poly_bm, + .iter_poly_mesh = extract_weights_iter_poly_mesh, + .finish = extract_weights_finish, + .data_type = 0, + .use_threading = true, + .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights), +}; /** \} */ -- cgit v1.2.3 From 507c19c0f7a1dc7a46ec8ac19d9e904c9e825a9b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 2 Jun 2021 17:15:05 +1000 Subject: Docs: formalize naming for generic callbacks in BKE_callbacks.h Add a doc-string explaining the purpose of each call back and how they should be used. Also add a currently unused callback 'POST_FAIL' that is to be used in cases the action fails - giving script authors, a guarantee that a call to `pre` will always have a matching `post/post_fail` call. - D11422: adds a callback that can use 'post_fail'. - T88696: proposed these conventions. --- source/blender/blenkernel/BKE_callbacks.h | 59 ++++++++++++++++++++++--- source/blender/python/intern/bpy_app_handlers.c | 3 ++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h index f04b5e45720..ef2a0ed34a0 100644 --- a/source/blender/blenkernel/BKE_callbacks.h +++ b/source/blender/blenkernel/BKE_callbacks.h @@ -30,11 +30,60 @@ struct Main; struct PointerRNA; /** - * Common suffix uses: - * - ``_PRE/_POST``: - * For handling discrete non-interactive events. - * - ``_INIT/_COMPLETE/_CANCEL``: - * For handling jobs (which may in turn cause other handlers to be called). + Callbacks for One Off Actions + * ============================= + * + * - `{ACTION}` use in cases where only a single callback is required, + * `VERSION_UPDATE` and `RENDER_STATS` for example. + * + * \note avoid single callbacks if there is a chance `PRE/POST` are useful to differentiate + * since renaming callbacks may break Python scripts. + * + * Callbacks for Common Actions + * ============================ + * + * - `{ACTION}_PRE` run before the action. + * - `{ACTION}_POST` run after the action. + * + * Optional Additional Callbacks + * ----------------------------- + * + * - `{ACTION}_INIT` when the handler may manipulate the context used to run the action. + * + * Examples where `INIT` functions may be useful are: + * + * - When rendering, an `INIT` function may change the camera or render settings, + * things which a `PRE` function can't support as this information has already been used. + * - When saving an `INIT` function could temporarily change the preferences. + * + * - `{ACTION}_POST_FAIL` should be included if the action may fail. + * + * Use this so a call to the `PRE` callback always has a matching call to `POST` or `POST_FAIL`. + * + * \note in most cases only `PRE/POST` are required. + * + * Callbacks for Background/Modal Tasks + * ==================================== + * + * - `{ACTION}_INIT` + * - `{ACTION}_COMPLETE` when a background job has finished. + * - `{ACTION}_CANCEL` When a background job is canceled partway through. + * + * While cancellation may be caused by any number of reasons, common causes may include: + * + * - Explicit user cancellation. + * - Exiting Blender. + * - Failure to acquire resources (such as disk-full, out of memory ... etc). + * + * \note `PRE/POST` handlers may be used along side modal task handlers + * as is the case for rendering, where rendering an animation uses modal task handlers, + * rendering a single frame has `PRE/POST` handlers. + * + * Python Access + * ============= + * + * All callbacks here must be exposed via the Python module `bpy.app.handlers`, + * see `bpy_app_handlers.c`. */ typedef enum { BKE_CB_EVT_FRAME_CHANGE_PRE, diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index a0b543097e6..bc05c51414f 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -43,6 +43,9 @@ void bpy_app_generic_callback(struct Main *main, static PyTypeObject BlenderAppCbType; +/** + * See `BKE_callbacks.h` #eCbEvent declaration for the policy on naming. + */ static PyStructSequence_Field app_cb_info_fields[] = { {"frame_change_pre", "Called after frame change for playback and rendering, before any data is evaluated for the " -- cgit v1.2.3 From 46447594de3714ef10725e921008e583008cc97e Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 2 Jun 2021 10:05:39 +0200 Subject: Fix T88567: Cryptomatte only works for the first View Layer. The view layer was always set to 0. This patch increments it. --- source/blender/compositor/nodes/COM_CryptomatteNode.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index 3beb3aa2917..4032a655633 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -118,9 +118,9 @@ void CryptomatteNode::input_operations_from_render_source( return; } - const short cryptomatte_layer_id = 0; + short view_layer_id = 0; const std::string prefix = prefix_from_node(context, node); - LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { + LISTBASE_FOREACH_INDEX (ViewLayer *, view_layer, &scene->view_layers, view_layer_id) { RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name); if (render_layer) { LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) { @@ -129,7 +129,7 @@ void CryptomatteNode::input_operations_from_render_source( RenderLayersProg *op = new RenderLayersProg( render_pass->name, DataType::Color, render_pass->channels); op->setScene(scene); - op->setLayerId(cryptomatte_layer_id); + op->setLayerId(view_layer_id); op->setRenderData(context.getRenderData()); op->setViewName(context.getViewName()); r_input_operations.append(op); -- cgit v1.2.3 From facc62d0d55907dd43d0846697fdf3b59f11b5f4 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 2 Jun 2021 13:21:47 +0200 Subject: Docs: 2.93 release description for Linux appdata --- release/freedesktop/org.blender.Blender.appdata.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/release/freedesktop/org.blender.Blender.appdata.xml b/release/freedesktop/org.blender.Blender.appdata.xml index f6d17834150..7a5a252e4ca 100644 --- a/release/freedesktop/org.blender.Blender.appdata.xml +++ b/release/freedesktop/org.blender.Blender.appdata.xml @@ -40,6 +40,25 @@ + + +

New features:

+
    +
  • Mesh primitive nodes
  • +
  • Line Art
  • +
  • EEVEE Realistic depth of field and volumetrics
  • +
  • Spreadsheet editor
  • +
+

Enhancements:

+
    +
  • Geometry nodes 22 new nodes and imrpoved attribute search
  • +
  • Mask loops, textures and patterns for sculpting
  • +
  • Grease pencil interpolate refactored and SVG and PDF support
  • +
  • Persistent Data rendering settings for Cycles
  • +
  • Video Sequencer Editor auto-proxy system
  • +
+
+

New features:

-- cgit v1.2.3 From 69e15eb1e4a520764ca30c82dcadffe42c96707e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 2 Jun 2021 21:29:15 +1000 Subject: Object: support running transfer mode in any object mode There is no need to limit this to sculpt mode, prepare for key short cut changes, see: T88092. --- source/blender/editors/object/object_modes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index dcb294bcb12..edf9afd1fd6 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -419,7 +419,7 @@ static bool object_transfer_mode_poll(bContext *C) return false; } const Object *ob = CTX_data_active_object(C); - return ob && (ob->mode & (OB_MODE_SCULPT)); + return ob && (ob->mode != OB_MODE_OBJECT); } /* Update the viewport rotation origin to the mouse cursor. */ -- cgit v1.2.3 From f92f5d1ac62c66ceb7a6ac1ff69084fbd5e3614a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 2 Jun 2021 21:32:25 +1000 Subject: Keymap: use D-Key for view-pie menu Use the D-key to access the view menu instead of the Tilda key, which isn't accessible on some international layouts. To resolve the conflicts the following changes have been made. - `D` (motion) opens the view menu. - `D` (mouse-button) uses grease-pencil (as it does currently). - `Tilda` is used to for "Object Mode Transfer" instead of the View menu. See T88092 for details. Reviewed By: Severin Ref D11189 --- release/scripts/presets/keyconfig/Blender.py | 6 ++-- .../keyconfig/keymap_data/blender_default.py | 40 ++++++++++++++++------ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index eb66c961472..222ee43432f 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -103,8 +103,8 @@ class Prefs(bpy.types.KeyConfigPreferences): v3d_tilde_action: EnumProperty( name="Tilde Action", items=( - ('VIEW', "Navigate", - "View operations (useful for keyboards without a numpad)", + ('OBJECT_SWITCH', "Object Switch", + "Switch the active object under the cursor (when not in object mode)", 0), ('GIZMO', "Gizmos", "Control transform gizmos", @@ -113,7 +113,7 @@ class Prefs(bpy.types.KeyConfigPreferences): description=( "Action when 'Tilde' is pressed" ), - default='VIEW', + default='OBJECT_SWITCH', update=update_fn, ) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index d63ae7a2745..c476d488724 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1078,17 +1078,11 @@ def km_view3d(params): {"properties": [("use_all_regions", True), ("center", False)]}), ("view3d.view_all", {"type": 'C', "value": 'PRESS', "shift": True}, {"properties": [("center", True)]}), - op_menu_pie( - "VIEW3D_MT_view_pie" if params.v3d_tilde_action == 'VIEW' else "VIEW3D_MT_transform_gizmo_pie", - {"type": 'ACCENT_GRAVE', "value": params.pie_value}, - ), - *(() if not params.use_pie_click_drag else - (("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'CLICK'}, None),)), - ("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None), + op_menu_pie("VIEW3D_MT_view_pie", {"type": 'D', "value": 'CLICK_DRAG'}), # Numpad views. ("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None), - ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS'}, - {"properties": [("type", 'FRONT')]}), + ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS'}, + {"properties": [("type", 'FRONT')]}), ("view3d.view_orbit", {"type": 'NUMPAD_2', "value": 'PRESS', "repeat": True}, {"properties": [("type", 'ORBITDOWN')]}), ("view3d.view_axis", {"type": 'NUMPAD_3', "value": 'PRESS'}, @@ -1327,6 +1321,32 @@ def km_view3d(params): op_tool_cycle("builtin.select_box", {"type": 'W', "value": 'PRESS'}), ]) + # Tilda key. + if params.use_pie_click_drag: + items.extend([ + ("object.transfer_mode", + {"type": 'ACCENT_GRAVE', "value": 'CLICK' if params.use_pie_click_drag else 'PRESS'}, + None), + op_menu_pie( + "VIEW3D_MT_transform_gizmo_pie", + {"type": 'ACCENT_GRAVE', "value": 'CLICK_DRAG'}, + ) + ]) + else: + if params.v3d_tilde_action == 'OBJECT_SWITCH': + items.append( + ("object.transfer_mode", + {"type": 'ACCENT_GRAVE', "value": 'PRESS'}, + {"properties": [("use_eyedropper", False)]}) + ) + else: + items.append( + op_menu_pie( + "VIEW3D_MT_transform_gizmo_pie", + {"type": 'ACCENT_GRAVE', "value": 'PRESS'}, + ) + ) + return keymap @@ -4484,8 +4504,6 @@ def km_sculpt(params): ) items.extend([ - # Transfer Sculpt Mode (release to avoid conflict with grease pencil drawing). - ("object.transfer_mode", {"type": 'D', "value": 'RELEASE'}, None), # Brush strokes ("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": [("mode", 'NORMAL')]}), -- cgit v1.2.3 From 6cd64f8caacc1a7c679d962a053f73263fc18c2c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 2 Jun 2021 14:13:40 +0200 Subject: Add workaround for gcc 11 compiler bug Differential Revision: https://developer.blender.org/D11462 --- intern/cycles/render/alembic_read.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp index 4d09c40b07b..33ce3502879 100644 --- a/intern/cycles/render/alembic_read.cpp +++ b/intern/cycles/render/alembic_read.cpp @@ -606,7 +606,8 @@ void read_geometry_data(AlembicProcedural *proc, template struct value_type_converter { using cycles_type = float; - static constexpr TypeDesc type_desc = TypeFloat; + /* Use `TypeDesc::FLOAT` instead of `TypeFloat` to work around a compiler bug in gcc 11. */ + static constexpr TypeDesc type_desc = TypeDesc::FLOAT; static constexpr const char *type_name = "float (default)"; static cycles_type convert_value(T value) -- cgit v1.2.3 From 34f99bc6be3c343dd371e1f877e972cc80dd396d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 2 Jun 2021 08:24:42 -0400 Subject: Geometry Nodes: Allow reading converted attribute directly from spline Often it would be beneficial to avoid the virtual array implementation in `geometry_component_curve.cc` that flattens an attribute for every spline and instead read an attribute separately for every input spline. This commit implements functions to do that. The downside is some code duplication-- we now have two places handling this conversion. However, we can head in this general direction for the attribute API anyway and support accessing attributes in smaller contiguous chunks where necessary. No functional changes in this commit. Differential Revision: https://developer.blender.org/D11456 --- source/blender/blenkernel/BKE_attribute_access.hh | 15 ++++++++++++ .../blender/blenkernel/intern/attribute_access.cc | 28 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index c3bc4d3ca4a..381a03d29d7 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -333,6 +333,21 @@ class CustomDataAttributes { void reallocate(const int size); std::optional get_for_read(const blender::StringRef name) const; + + blender::fn::GVArrayPtr get_for_read(const StringRef name, + const CustomDataType data_type, + const void *default_value) const; + + template + blender::fn::GVArray_Typed get_for_read(const blender::StringRef name, + 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); + 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); diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d36e9ed3e86..1495eb23254 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -46,6 +46,8 @@ using blender::StringRef; using blender::StringRefNull; using blender::fn::GMutableSpan; using blender::fn::GSpan; +using blender::fn::GVArray_For_GSpan; +using blender::fn::GVArray_For_SingleValue; namespace blender::bke { @@ -628,6 +630,32 @@ std::optional CustomDataAttributes::get_for_read(const StringRef name) co return {}; } +/** + * Return a virtual array for a stored attribute, or a single value virtual array with the default + * 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, + 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); + if (!attribute) { + const int domain_size = this->size_; + return std::make_unique( + *type, domain_size, (default_value == nullptr) ? type->default_value() : default_value); + } + + if (attribute->type() == *type) { + return std::make_unique(*attribute); + } + const blender::nodes::DataTypeConversions &conversions = + blender::nodes::get_implicit_type_conversions(); + return conversions.try_convert(std::make_unique(*attribute), *type); +} + std::optional CustomDataAttributes::get_for_write(const StringRef name) { BLI_assert(size_ != 0); -- cgit v1.2.3 From ea6d099082caeaff42843661688bc704ede5cfd0 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 2 Jun 2021 08:27:53 -0400 Subject: Cleanup: Clang format --- source/blender/draw/intern/draw_cache_extract_mesh_private.h | 3 +-- source/blender/draw/intern/draw_cache_extract_mesh_render_data.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index d3acf10b72c..644354989d8 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -454,8 +454,7 @@ MeshRenderData *mesh_render_data_create(Mesh *me, const ToolSettings *ts, const eMRIterType iter_type); void mesh_render_data_free(MeshRenderData *mr); -void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRDataType data_flag); +void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag); void mesh_render_data_update_looptris(MeshRenderData *mr, const eMRIterType iter_type, const eMRDataType data_flag); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c index db28e4f3ba3..4741bcb06c9 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -150,8 +150,7 @@ void mesh_render_data_update_looptris(MeshRenderData *mr, } } -void mesh_render_data_update_normals(MeshRenderData *mr, - const eMRDataType data_flag) +void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag) { Mesh *me = mr->me; const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0; -- cgit v1.2.3 From a55b73417f023a74c62773a6dc467146a2218519 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 2 Jun 2021 08:28:46 -0400 Subject: Cleanup: Correct comments This corrects an outdated comment in the vector header and a typo in the index mask header. --- source/blender/blenlib/BLI_index_mask.hh | 2 +- source/blender/blenlib/BLI_vector.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index 48b01edcd6f..f04c0e9c80a 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -110,7 +110,7 @@ class IndexMask { } /** - * Returns the n-th index referenced by this IndexMask. The `index_mask` method returns an + * Returns the n-th index referenced by this IndexMask. The `index_range` method returns an * IndexRange containing all indices that can be used as parameter here. */ int64_t operator[](int64_t n) const diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 8bea2584ca7..f6f546c90b9 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -161,7 +161,7 @@ class Vector { } /** - * Create a vector from an array ref. The values in the vector are copy constructed. + * Create a vector from a span. The values in the vector are copy constructed. */ template> * = nullptr> Vector(Span values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator) -- cgit v1.2.3 From 5b176b66da1f85f19e042f69dc302e8e72e0dc44 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Wed, 2 Jun 2021 20:55:54 +0800 Subject: LineArt: Tolerance for faces perpendicular to view This is due to cam->obmat precision issue, where it affects view vector precision. Reviewed by Sebastian Parborg (zeddb) Differential Revision: https://developer.blender.org/D11461 --- source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 0b439c20d65..b546aaa683e 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -2064,7 +2064,11 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), dot_r = dot_v3v3_db(Rv, tri->gn); dot_f = dot_v3v3_db(Cv, tri->gn); - if (!dot_f) { + /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possibile that _some_ + * faces in perspective mode would get erroneously caught in this condition where they really are + * legit faces that would produce occlusion, but haven't encountered those yet in my test files. + */ + if (fabs(dot_f) < FLT_EPSILON) { return false; } -- cgit v1.2.3 From 21de669141dc8cde86eb8edfb18ab614c2b3109e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 2 Jun 2021 09:11:35 -0400 Subject: Splines: Function to copy spline settings without data Often you need to copy a spline to do an operation, but don't want to manually copy over all of the settings. I've already forgotten to do it once anyway. These functions copy a spline's "settings" into a new spline, but not the data. I tried to avoid duplicating any copying so this is easier to extend in the future. Differential Revision: https://developer.blender.org/D11463 --- source/blender/blenkernel/BKE_spline.hh | 18 +++++++++++++----- source/blender/blenkernel/intern/spline_bezier.cc | 8 ++++++++ source/blender/blenkernel/intern/spline_nurbs.cc | 10 ++++++++++ source/blender/blenkernel/intern/spline_poly.cc | 7 +++++++ 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index ef76c699cbb..fc1292e3620 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -101,15 +101,14 @@ class Spline { Spline(const Type type) : type_(type) { } - Spline(Spline &other) - : normal_mode(other.normal_mode), - attributes(other.attributes), - type_(other.type_), - is_cyclic_(other.is_cyclic_) + Spline(Spline &other) : attributes(other.attributes), type_(other.type_) { + copy_base_settings(other, *this); } virtual SplinePtr copy() const = 0; + /** Return a new spline with the same type and settings like "cyclic", but without any data. */ + virtual SplinePtr copy_settings() const = 0; Spline::Type type() const; @@ -183,6 +182,12 @@ class Spline { protected: virtual void correct_end_tangents() const = 0; + /** Copy settings stored in the base spline class. */ + static void copy_base_settings(const Spline &src, Spline &dst) + { + dst.normal_mode = src.normal_mode; + dst.is_cyclic_ = src.is_cyclic_; + } }; /** @@ -236,6 +241,7 @@ class BezierSpline final : public Spline { public: virtual SplinePtr copy() const final; + SplinePtr copy_settings() const final; BezierSpline() : Spline(Type::Bezier) { } @@ -377,6 +383,7 @@ class NURBSpline final : public Spline { public: SplinePtr copy() const final; + SplinePtr copy_settings() const final; NURBSpline() : Spline(Type::NURBS) { } @@ -444,6 +451,7 @@ class PolySpline final : public Spline { public: SplinePtr copy() const final; + SplinePtr copy_settings() const final; PolySpline() : Spline(Type::Poly) { } diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 4f2625c14d3..3c6cf2c78cf 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -31,6 +31,14 @@ SplinePtr BezierSpline::copy() const return std::make_unique(*this); } +SplinePtr BezierSpline::copy_settings() const +{ + std::unique_ptr copy = std::make_unique(); + copy_base_settings(*this, *copy); + copy->resolution_ = resolution_; + return copy; +} + int BezierSpline::size() const { const int size = positions_.size(); diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index ae691d26cdb..cae206341a0 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -32,6 +32,16 @@ SplinePtr NURBSpline::copy() const return std::make_unique(*this); } +SplinePtr NURBSpline::copy_settings() const +{ + std::unique_ptr copy = std::make_unique(); + copy_base_settings(*this, *copy); + copy->knots_mode = knots_mode; + copy->resolution_ = resolution_; + copy->order_ = order_; + return copy; +} + int NURBSpline::size() const { const int size = positions_.size(); diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index 5c33b0052fc..5f8e81d5ad0 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -28,6 +28,13 @@ SplinePtr PolySpline::copy() const return std::make_unique(*this); } +SplinePtr PolySpline::copy_settings() const +{ + std::unique_ptr copy = std::make_unique(); + copy_base_settings(*this, *copy); + return copy; +} + int PolySpline::size() const { const int size = positions_.size(); -- cgit v1.2.3 From f944121700daf267766399ef368834be3cdec518 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Wed, 2 Jun 2021 16:41:13 +0200 Subject: GPencil: New operator to Normalize strokes Sometimes is required to reset the thickness or the opacity of the strokes. Actually this was done using a modifier, but this operators solves this. Reviewed By: mendio, filedescriptor Maniphest Tasks: T87427 Differential Revision: https://developer.blender.org/D11453 --- release/scripts/startup/bl_ui/space_view3d.py | 4 + source/blender/editors/gpencil/gpencil_edit.c | 178 ++++++++++++++++++++++++ source/blender/editors/gpencil/gpencil_intern.h | 1 + source/blender/editors/gpencil/gpencil_ops.c | 1 + 4 files changed, 184 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index ae18fc4fb76..229b520e449 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -5035,6 +5035,10 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu): layout.operator("gpencil.stroke_flip", text="Switch Direction") layout.prop(settings, "use_scale_thickness", text="Scale Thickness") + layout.separator() + layout.operator("gpencil.stroke_normalize", text="Normalize Thickness").mode = 'THICKNESS' + layout.operator("gpencil.stroke_normalize", text="Normalize Opacity").mode = 'OPACITY' + layout.separator() layout.operator("gpencil.reset_transform_fill", text="Reset Fill Transform") diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index e65afa1abff..dc0508bd608 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -5347,3 +5347,181 @@ void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Stroke Normalize Operator + * \{ */ + +typedef enum eGP_NormalizeMode { + GP_NORMALIZE_THICKNESS = 0, + GP_NORMALIZE_OPACITY, +} eGP_NormalizeMode; + +static bool gpencil_stroke_normalize_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + bGPdata *gpd = (bGPdata *)ob->data; + if (gpd == NULL) { + return false; + } + + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + + return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL)); +} + +static void gpencil_stroke_normalize_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *row; + + const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode"); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "mode", 0, NULL, ICON_NONE); + + if (mode == GP_NORMALIZE_THICKNESS) { + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "value", 0, NULL, ICON_NONE); + } + else if (mode == GP_NORMALIZE_OPACITY) { + row = uiLayoutRow(layout, true); + uiItemR(row, op->ptr, "factor", 0, NULL, ICON_NONE); + } +} + +static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* Sanity checks. */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + + const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode"); + const int value = RNA_int_get(op->ptr, "value"); + const float factor = RNA_float_get(op->ptr, "factor"); + + /* Go through each editable + selected stroke. */ + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + + if (gpf == NULL) { + continue; + } + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + bool selected = (is_curve_edit) ? gps->editcurve->flag |= GP_CURVE_SELECT : + (gps->flag & GP_STROKE_SELECT); + if (!selected) { + continue; + } + + float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1); + /* Fill opacity need to be managed before. */ + if (mode == GP_NORMALIZE_OPACITY) { + gps->fill_opacity_fac = factor; + CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f); + } + + /* Loop all Polyline points. */ + if (!is_curve_edit) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (mode == GP_NORMALIZE_THICKNESS) { + pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f); + } + else if (mode == GP_NORMALIZE_OPACITY) { + pt->strength = factor; + CLAMP(pt->strength, 0.0f, 1.0f); + } + } + } + else { + /* Loop all Bezier points. */ + for (int i = 0; i < gps->editcurve->tot_curve_points; i++) { + bGPDcurve_point *gpc_pt = &gps->editcurve->curve_points[i]; + if (mode == GP_NORMALIZE_THICKNESS) { + gpc_pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f); + } + else if (mode == GP_NORMALIZE_OPACITY) { + gpc_pt->strength = factor; + CLAMP(gpc_pt->strength, 0.0f, 1.0f); + } + } + + gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE; + BKE_gpencil_stroke_geometry_update(gpd, gps); + } + } + /* If not multiedit, exit loop. */ + if (!is_multiedit) { + break; + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_normalize(wmOperatorType *ot) +{ + static const EnumPropertyItem prop_gpencil_normalize_modes[] = { + {GP_NORMALIZE_THICKNESS, + "THICKNESS", + 0, + "Thickness", + "Normalizes the stroke thickness by making all points use the same thickness value"}, + {GP_NORMALIZE_OPACITY, + "OPACITY", + 0, + "Opacity", + "Normalizes the stroke opacity by making all points use the same opacity value"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Normalize Stroke"; + ot->idname = "GPENCIL_OT_stroke_normalize"; + ot->description = "Normalize stroke attributes"; + + /* api callbacks */ + ot->exec = gpencil_stroke_normalize_exec; + ot->poll = gpencil_stroke_normalize_poll; + ot->ui = gpencil_stroke_normalize_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + ot->prop = RNA_def_enum( + ot->srna, "mode", prop_gpencil_normalize_modes, 0, "Mode", "Attribute to be normalized"); + RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f); + RNA_def_int(ot->srna, "value", 10, 0, 1000, "Value", "Value", 0, 1000); +} + +/** \} */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 0e8fdc3c375..276e8c81e0f 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -488,6 +488,7 @@ void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot); void GPENCIL_OT_stroke_reset_vertex_color(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_normalize(struct wmOperatorType *ot); void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot); void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 0e9ce1d603f..35640cf3b66 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -649,6 +649,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_merge_by_distance); WM_operatortype_append(GPENCIL_OT_stroke_merge_material); WM_operatortype_append(GPENCIL_OT_stroke_reset_vertex_color); + WM_operatortype_append(GPENCIL_OT_stroke_normalize); WM_operatortype_append(GPENCIL_OT_material_to_vertex_color); WM_operatortype_append(GPENCIL_OT_extract_palette_vertex); -- cgit v1.2.3 From 730a46e87d261543c9550ddef406894e14d5bea6 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 2 Jun 2021 16:53:39 +0200 Subject: EEVEE: AOVs not same as cycles. EEVEE uses hashing to sync aov names and types with the gpu. For the type a hashed value was overridden making `decalA` and `decalB` choose the same hash. This patches fixes this by removing the most significant bit. --- release/scripts/addons | 2 +- source/blender/draw/engines/eevee/eevee_renderpasses.c | 2 +- source/blender/nodes/shader/nodes/node_shader_output_aov.c | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/release/scripts/addons b/release/scripts/addons index 27fe7f3a4f9..cdabac54c4f 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 27fe7f3a4f964b53af436c4da4ddea337eff0c7e +Subproject commit cdabac54c4fe7c6f8df125814442762aa539172b diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index 5739024993e..5ada53ab98c 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -79,7 +79,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata) * type the rest of the bits are used for the name hash. */ int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov) { - int hash = BLI_hash_string(aov->name); + int hash = BLI_hash_string(aov->name) << 1; SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK); return hash; } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.c index 403b3e6d9d6..7e7e1b703f1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_aov.c +++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.c @@ -43,8 +43,9 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat, { GPUNodeLink *outlink; NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage; - /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash`. */ - unsigned int hash = BLI_hash_string(aov->name) & ~1; + /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and + * `EEVEE_renderpasses_aov_hash`. */ + unsigned int hash = BLI_hash_string(aov->name) << 1; GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink); GPU_material_add_output_link_aov(mat, outlink, hash); -- cgit v1.2.3 From 2489f72d79401fdefa6cf70d5acde806096b7b4a Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 2 Jun 2021 16:56:10 +0200 Subject: Revert "EEVEE: AOVs not same as cycles." This reverts commit 730a46e87d261543c9550ddef406894e14d5bea6. --- release/scripts/addons | 2 +- source/blender/draw/engines/eevee/eevee_renderpasses.c | 2 +- source/blender/nodes/shader/nodes/node_shader_output_aov.c | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/release/scripts/addons b/release/scripts/addons index cdabac54c4f..27fe7f3a4f9 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit cdabac54c4fe7c6f8df125814442762aa539172b +Subproject commit 27fe7f3a4f964b53af436c4da4ddea337eff0c7e diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index 5ada53ab98c..5739024993e 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -79,7 +79,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata) * type the rest of the bits are used for the name hash. */ int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov) { - int hash = BLI_hash_string(aov->name) << 1; + int hash = BLI_hash_string(aov->name); SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK); return hash; } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.c index 7e7e1b703f1..403b3e6d9d6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_aov.c +++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.c @@ -43,9 +43,8 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat, { GPUNodeLink *outlink; NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage; - /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and - * `EEVEE_renderpasses_aov_hash`. */ - unsigned int hash = BLI_hash_string(aov->name) << 1; + /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash`. */ + unsigned int hash = BLI_hash_string(aov->name) & ~1; GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink); GPU_material_add_output_link_aov(mat, outlink, hash); -- cgit v1.2.3 From 7654203cc8b6b81013eeef558d6d15a9344fb9d1 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 2 Jun 2021 16:58:36 +0200 Subject: EEVEE: AOVs not same as cycles. EEVEE uses hashing to sync aov names and types with the gpu. For the type a hashed value was overridden making `decalA` and `decalB` choose the same hash. This patches fixes this by removing the most significant bit. --- source/blender/draw/engines/eevee/eevee_renderpasses.c | 2 +- source/blender/nodes/shader/nodes/node_shader_output_aov.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index 5739024993e..5ada53ab98c 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -79,7 +79,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata) * type the rest of the bits are used for the name hash. */ int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov) { - int hash = BLI_hash_string(aov->name); + int hash = BLI_hash_string(aov->name) << 1; SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK); return hash; } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.c index 403b3e6d9d6..7e7e1b703f1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_aov.c +++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.c @@ -43,8 +43,9 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat, { GPUNodeLink *outlink; NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage; - /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash`. */ - unsigned int hash = BLI_hash_string(aov->name) & ~1; + /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and + * `EEVEE_renderpasses_aov_hash`. */ + unsigned int hash = BLI_hash_string(aov->name) << 1; GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink); GPU_material_add_output_link_aov(mat, outlink, hash); -- cgit v1.2.3 From 05b685989b5288bcfdc5bd4ae5c387a1da4dd58e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 2 Jun 2021 11:11:34 -0400 Subject: Fix T88732: Curve to mesh node crash with empty input curve The mesh to curve node generated an empty curve because no edges were selected. This commit changes that node to not add a curve in that case. This also changes the curve to mesh node to not add a material when no mesh was created. Even though we don't expect null curves or meshes in this case, the change is harmless. --- source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 6 ++---- 2 files changed, 3 insertions(+), 5 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 8979949736c..fe1b23bf6d7 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 @@ -237,6 +237,7 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr } Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total); + BKE_id_material_eval_ensure_default_slot(&mesh->id); MutableSpan verts{mesh->mvert, mesh->totvert}; MutableSpan edges{mesh->medge, mesh->totedge}; MutableSpan loops{mesh->mloop, mesh->totloop}; @@ -297,7 +298,6 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(), (profile_curve == nullptr) ? vert_curve : *profile_curve); - BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } 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 b852f929b5f..0fb7910c904 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 @@ -291,14 +291,12 @@ static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) const MeshComponent &component = *geometry_set.get_component_for_read(); const Mesh &mesh = *component.get_for_read(); Span verts = Span{mesh.mvert, mesh.totvert}; - Span edges = Span{mesh.medge, mesh.totedge}; - if (edges.size() == 0) { + Vector> selected_edges = get_selected_edges(params, component); + if (selected_edges.size() == 0) { params.set_output("Curve", GeometrySet()); return; } - Vector> selected_edges = get_selected_edges(params, component); - CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges); copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps); -- cgit v1.2.3 From ae28ceb9d86e9db702d968ed779e9bb2b2d31a08 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 3 Jun 2021 00:53:34 +1000 Subject: Fix swapped x/y in event simulation script Incorrect area center calculation, also correct comments. --- tests/python/bl_run_operators_event_simulate.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/python/bl_run_operators_event_simulate.py b/tests/python/bl_run_operators_event_simulate.py index e536b9da2b1..251d27fce90 100644 --- a/tests/python/bl_run_operators_event_simulate.py +++ b/tests/python/bl_run_operators_event_simulate.py @@ -31,7 +31,7 @@ Possible use cases for this script include: - Creating reproducible user interactions for the purpose of benchmarking and profiling. - Note that curse-motion actions report the update time between events + Note that cursor-motion actions report the update time between events which can be helpful when measuring optimizations. - As a convenient way to replay interactive actions that reproduce a bug. @@ -86,7 +86,7 @@ Sculpt stroke: 'event(type="WHEELDOWNMOUSE", value="TAP", repeat=2)' \ 'event(type="LEFTMOUSE", value="PRESS")' \ 'cursor_motion(path="CIRCLE", radius=300, steps=100, repeat=5)' \ - 'event(type="LEFTMOUSE", value="RELEASE")' \ + 'event(type="LEFTMOUSE", value="RELEASE")' Implementation @@ -245,8 +245,8 @@ class action_handlers: if area is None: raise ArgumentTypeError("Area with ui_type=%r not found" % ui_type) - x = area.y + (area.width // 2) - y = area.x + (area.height // 2) + x = area.x + (area.width // 2) + y = area.y + (area.height // 2) yield dict(type='MOUSEMOVE', value='NOTHING', x=x, y=y) yield dict(type='SPACE', value='TAP', ctrl=True, alt=True) @@ -549,8 +549,8 @@ def main_event_iter(*, action_list): """ area = find_main_area() - x_init = area.y + (area.width // 2) - y_init = area.x + (area.height // 2) + x_init = area.x + (area.width // 2) + y_init = area.y + (area.height // 2) yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init) -- cgit v1.2.3 From b8ae30e9e33312da1f38f4187800002eaa957295 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 3 Jun 2021 01:12:00 +1000 Subject: Cleanup: unused variable --- source/blender/editors/gpencil/gpencil_edit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index dc0508bd608..e01221db277 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -5373,7 +5373,7 @@ static bool gpencil_stroke_normalize_poll(bContext *C) return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL)); } -static void gpencil_stroke_normalize_ui(bContext *C, wmOperator *op) +static void gpencil_stroke_normalize_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; uiLayout *row; -- cgit v1.2.3 From 4f8edc8e7fc9389b03543bc9a20f6c936e32b531 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 2 Jun 2021 17:19:36 +0200 Subject: Nodes: move some files to C++ This just moves a couple of files in `space_node` to C++ and fixes related errors. The goal is to be able to use C++ data structures to simplify the code. Differential Revision: https://developer.blender.org/D11451 --- source/blender/editors/space_node/CMakeLists.txt | 18 +- source/blender/editors/space_node/drawnode.c | 4152 ------------------- source/blender/editors/space_node/drawnode.cc | 4261 ++++++++++++++++++++ source/blender/editors/space_node/node_add.c | 1019 ----- source/blender/editors/space_node/node_add.cc | 1019 +++++ source/blender/editors/space_node/node_edit.c | 2834 ------------- source/blender/editors/space_node/node_edit.cc | 2835 +++++++++++++ source/blender/editors/space_node/node_group.c | 1132 ------ source/blender/editors/space_node/node_group.cc | 1134 ++++++ .../editors/space_node/node_relationships.c | 2316 ----------- .../editors/space_node/node_relationships.cc | 2321 +++++++++++ source/blender/editors/space_node/node_select.c | 1312 ------ source/blender/editors/space_node/node_select.cc | 1315 ++++++ source/blender/editors/space_node/node_templates.c | 892 ---- .../blender/editors/space_node/node_templates.cc | 895 ++++ source/blender/editors/space_node/node_toolbar.c | 39 - source/blender/editors/space_node/node_toolbar.cc | 39 + source/blender/editors/space_node/node_view.c | 700 ---- source/blender/editors/space_node/node_view.cc | 702 ++++ 19 files changed, 14530 insertions(+), 14405 deletions(-) delete mode 100644 source/blender/editors/space_node/drawnode.c create mode 100644 source/blender/editors/space_node/drawnode.cc delete mode 100644 source/blender/editors/space_node/node_add.c create mode 100644 source/blender/editors/space_node/node_add.cc delete mode 100644 source/blender/editors/space_node/node_edit.c create mode 100644 source/blender/editors/space_node/node_edit.cc delete mode 100644 source/blender/editors/space_node/node_group.c create mode 100644 source/blender/editors/space_node/node_group.cc delete mode 100644 source/blender/editors/space_node/node_relationships.c create mode 100644 source/blender/editors/space_node/node_relationships.cc delete mode 100644 source/blender/editors/space_node/node_select.c create mode 100644 source/blender/editors/space_node/node_select.cc delete mode 100644 source/blender/editors/space_node/node_templates.c create mode 100644 source/blender/editors/space_node/node_templates.cc delete mode 100644 source/blender/editors/space_node/node_toolbar.c create mode 100644 source/blender/editors/space_node/node_toolbar.cc delete mode 100644 source/blender/editors/space_node/node_view.c create mode 100644 source/blender/editors/space_node/node_view.cc diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index bc043a4e665..6e234c5b2ce 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -37,20 +37,20 @@ set(INC set(SRC - drawnode.c - node_add.c + drawnode.cc + node_add.cc node_buttons.c node_draw.cc - node_edit.c + node_edit.cc node_geometry_attribute_search.cc node_gizmo.c - node_group.c + node_group.cc node_ops.c - node_relationships.c - node_select.c - node_templates.c - node_toolbar.c - node_view.c + node_relationships.cc + node_select.cc + node_templates.cc + node_toolbar.cc + node_view.cc space_node.c node_intern.h diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c deleted file mode 100644 index f11f1c01019..00000000000 --- a/source/blender/editors/space_node/drawnode.c +++ /dev/null @@ -1,4152 +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. - */ - -/** \file - * \ingroup spnode - * \brief lower level node drawing for nodes (boarders, headers etc), also node layout. - */ - -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_system.h" -#include "BLI_threads.h" - -#include "DNA_node_types.h" -#include "DNA_object_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_text_types.h" -#include "DNA_userdef_types.h" - -#include "BKE_context.h" -#include "BKE_curve.h" -#include "BKE_image.h" -#include "BKE_main.h" -#include "BKE_node.h" -#include "BKE_tracking.h" - -#include "BLF_api.h" -#include "BLT_translation.h" - -#include "BIF_glutil.h" - -#include "GPU_batch.h" -#include "GPU_batch_presets.h" -#include "GPU_framebuffer.h" -#include "GPU_immediate.h" -#include "GPU_matrix.h" -#include "GPU_platform.h" -#include "GPU_state.h" - -#include "DRW_engine.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "ED_node.h" -#include "ED_space_api.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "UI_resources.h" -#include "UI_view2d.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf_types.h" - -#include "NOD_composite.h" -#include "NOD_geometry.h" -#include "NOD_shader.h" -#include "NOD_texture.h" -#include "node_intern.h" /* own include */ - -/* Default flags for uiItemR(). Name is kept short since this is used a lot in this file. */ -#define DEFAULT_FLAGS UI_ITEM_R_SPLIT_EMPTY_NAME - -/* ****************** SOCKET BUTTON DRAW FUNCTIONS ***************** */ - -static void node_socket_button_label(bContext *UNUSED(C), - uiLayout *layout, - PointerRNA *UNUSED(ptr), - PointerRNA *UNUSED(node_ptr), - const char *text) -{ - uiItemL(layout, text, 0); -} - -/* ****************** BUTTON CALLBACKS FOR ALL TREES ***************** */ - -static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNode *node = ptr->data; - /* first output stores value */ - bNodeSocket *output = node->outputs.first; - PointerRNA sockptr; - RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); - - uiItemR(layout, &sockptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNode *node = ptr->data; - /* first output stores value */ - bNodeSocket *output = node->outputs.first; - PointerRNA sockptr; - uiLayout *col; - RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); - - col = uiLayoutColumn(layout, false); - uiTemplateColorPicker(col, &sockptr, "default_value", 1, 0, 0, 0); - uiItemR(col, &sockptr, "default_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); -} - -static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - - uiLayout *col = uiLayoutColumn(layout, false); - uiLayout *row = uiLayoutRow(col, true); - uiItemR(row, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE); - if (ELEM(ntree->type, NTREE_COMPOSIT, NTREE_TEXTURE)) { - uiItemR(row, ptr, "use_alpha", DEFAULT_FLAGS, "", ICON_IMAGE_RGB_ALPHA); - } - - uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ -#if 0 - /* XXX no context access here .. */ - bNode *node = ptr->data; - CurveMapping *cumap = node->storage; - - if (cumap) { - cumap->flag |= CUMA_DRAW_CFRA; - if (node->custom1 < node->custom2) { - cumap->sample[0] = (float)(CFRA - node->custom1) / (float)(node->custom2 - node->custom1); - } - } -#endif - - uiTemplateCurveMapping(layout, ptr, "curve", 's', false, false, false, false); - - uiLayout *row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "frame_start", DEFAULT_FLAGS, IFACE_("Sta"), ICON_NONE); - uiItemR(row, ptr, "frame_end", DEFAULT_FLAGS, IFACE_("End"), ICON_NONE); -} - -static void node_buts_colorramp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiTemplateColorRamp(layout, ptr, "color_ramp", 0); -} - -static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false); -} - -#define SAMPLE_FLT_ISNONE FLT_MAX -/* bad bad, 2.5 will do better?... no it won't... */ -static float _sample_col[4] = {SAMPLE_FLT_ISNONE}; -void ED_node_sample_set(const float col[4]) -{ - if (col) { - copy_v4_v4(_sample_col, col); - } - else { - copy_v4_fl(_sample_col, SAMPLE_FLT_ISNONE); - } -} - -static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNode *node = ptr->data; - CurveMapping *cumap = node->storage; - - if (_sample_col[0] != SAMPLE_FLT_ISNONE) { - cumap->flag |= CUMA_DRAW_SAMPLE; - copy_v3_v3(cumap->sample, _sample_col); - } - else { - cumap->flag &= ~CUMA_DRAW_SAMPLE; - } - - /* "Tone" (Standard/Film-like) only used in the Compositor. */ - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - uiTemplateCurveMapping( - layout, ptr, "mapping", 'c', false, false, false, (ntree->type == NTREE_COMPOSIT)); -} - -static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNode *node = ptr->data; - /* first output stores normal */ - bNodeSocket *output = node->outputs.first; - PointerRNA sockptr; - RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); - - uiItemR(layout, &sockptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNode *node = ptr->data; - - short multi = (node->id && ((Tex *)node->id)->use_nodes && (node->type != CMP_NODE_TEXTURE) && - (node->type != TEX_NODE_TEXTURE)); - - uiItemR(layout, ptr, "texture", DEFAULT_FLAGS, "", ICON_NONE); - - if (multi) { - /* Number Drawing not optimal here, better have a list*/ - uiItemR(layout, ptr, "node_output", DEFAULT_FLAGS, "", ICON_NONE); - } -} - -static void node_shader_buts_clamp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "clamp_type", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "interpolation_type", DEFAULT_FLAGS, "", ICON_NONE); - if (!ELEM(RNA_enum_get(ptr, "interpolation_type"), - NODE_MAP_RANGE_SMOOTHSTEP, - NODE_MAP_RANGE_SMOOTHERSTEP)) { - uiItemR(layout, ptr, "clamp", DEFAULT_FLAGS, NULL, ICON_NONE); - } -} - -static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static int node_resize_area_default(bNode *node, int x, int y) -{ - if (node->flag & NODE_HIDDEN) { - rctf totr = node->totr; - /* right part of node */ - totr.xmin = node->totr.xmax - 20.0f; - if (BLI_rctf_isect_pt(&totr, x, y)) { - return NODE_RESIZE_RIGHT; - } - - return 0; - } - - const float size = NODE_RESIZE_MARGIN; - rctf totr = node->totr; - int dir = 0; - - if (x >= totr.xmax - size && x < totr.xmax && y >= totr.ymin && y < totr.ymax) { - dir |= NODE_RESIZE_RIGHT; - } - if (x >= totr.xmin && x < totr.xmin + size && y >= totr.ymin && y < totr.ymax) { - dir |= NODE_RESIZE_LEFT; - } - return dir; -} - -/* ****************** BUTTON CALLBACKS FOR COMMON NODES ***************** */ - -static void node_draw_buttons_group(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - uiTemplateIDBrowse( - layout, C, ptr, "node_tree", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, NULL); -} - -/* XXX Does a bounding box update by iterating over all children. - * Not ideal to do this in every draw call, but doing as transform callback doesn't work, - * since the child node totr rects are not updated properly at that point. - */ -static void node_draw_frame_prepare(const bContext *UNUSED(C), bNodeTree *ntree, bNode *node) -{ - const float margin = 1.5f * U.widget_unit; - NodeFrame *data = (NodeFrame *)node->storage; - - /* init rect from current frame size */ - rctf rect; - node_to_view(node, node->offsetx, node->offsety, &rect.xmin, &rect.ymax); - node_to_view( - node, node->offsetx + node->width, node->offsety - node->height, &rect.xmax, &rect.ymin); - - /* frame can be resized manually only if shrinking is disabled or no children are attached */ - data->flag |= NODE_FRAME_RESIZEABLE; - /* for shrinking bbox, initialize the rect from first child node */ - bool bbinit = (data->flag & NODE_FRAME_SHRINK); - /* fit bounding box to all children */ - LISTBASE_FOREACH (bNode *, tnode, &ntree->nodes) { - if (tnode->parent != node) { - continue; - } - - /* add margin to node rect */ - rctf noderect = tnode->totr; - noderect.xmin -= margin; - noderect.xmax += margin; - noderect.ymin -= margin; - noderect.ymax += margin; - - /* first child initializes frame */ - if (bbinit) { - bbinit = 0; - rect = noderect; - data->flag &= ~NODE_FRAME_RESIZEABLE; - } - else { - BLI_rctf_union(&rect, &noderect); - } - } - - /* now adjust the frame size from view-space bounding box */ - node_from_view(node, rect.xmin, rect.ymax, &node->offsetx, &node->offsety); - float xmax, ymax; - node_from_view(node, rect.xmax, rect.ymin, &xmax, &ymax); - node->width = xmax - node->offsetx; - node->height = -ymax + node->offsety; - - node->totr = rect; -} - -static void node_draw_frame_label(bNodeTree *ntree, bNode *node, const float aspect) -{ - /* XXX font id is crap design */ - const int fontid = UI_style_get()->widgetlabel.uifont_id; - NodeFrame *data = (NodeFrame *)node->storage; - const int font_size = data->label_size / aspect; - - char label[MAX_NAME]; - nodeLabel(ntree, node, label, sizeof(label)); - - BLF_enable(fontid, BLF_ASPECT); - BLF_aspect(fontid, aspect, aspect, 1.0f); - /* clamp otherwise it can suck up a LOT of memory */ - BLF_size(fontid, MIN2(24, font_size), U.dpi); - - /* title color */ - int color_id = node_get_colorid(node); - uchar color[3]; - UI_GetThemeColorBlendShade3ubv(TH_TEXT, color_id, 0.4f, 10, color); - BLF_color3ubv(fontid, color); - - const float margin = (float)(NODE_DY / 4); - const float width = BLF_width(fontid, label, sizeof(label)); - const float ascender = BLF_ascender(fontid); - const int label_height = ((margin / aspect) + (ascender * aspect)); - - /* 'x' doesn't need aspect correction */ - rctf *rct = &node->totr; - /* XXX a bit hacky, should use separate align values for x and y */ - 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); - - /* draw text body */ - if (node->id) { - Text *text = (Text *)node->id; - const int line_height_max = BLF_height_max(fontid); - const float line_spacing = (line_height_max * aspect); - const float line_width = (BLI_rctf_size_x(rct) - margin) / aspect; - - /* 'x' doesn't need aspect correction */ - x = rct->xmin + margin; - y = rct->ymax - (label_height + line_spacing); - /* early exit */ - int y_min = y + ((margin * 2) - (y - rct->ymin)); - - BLF_enable(fontid, BLF_CLIPPING | BLF_WORD_WRAP); - BLF_clipping(fontid, - rct->xmin, - /* round to avoid clipping half-way through a line */ - y - (floorf(((y - rct->ymin) - (margin * 2)) / line_spacing) * line_spacing), - rct->xmin + line_width, - rct->ymax); - - BLF_wordwrap(fontid, line_width); - - LISTBASE_FOREACH (TextLine *, line, &text->lines) { - struct ResultBLF info; - if (line->line[0]) { - BLF_position(fontid, x, y, 0); - BLF_draw_ex(fontid, line->line, line->len, &info); - y -= line_spacing * info.lines; - } - else { - y -= line_spacing; - } - if (y < y_min) { - break; - } - } - - BLF_disable(fontid, BLF_CLIPPING | BLF_WORD_WRAP); - } - - BLF_disable(fontid, BLF_ASPECT); -} - -static void node_draw_frame(const bContext *C, - ARegion *region, - SpaceNode *snode, - bNodeTree *ntree, - bNode *node, - bNodeInstanceKey UNUSED(key)) -{ - - /* skip if out of view */ - if (BLI_rctf_isect(&node->totr, ®ion->v2d.cur, NULL) == false) { - UI_block_end(C, node->block); - node->block = NULL; - return; - } - - float color[4]; - UI_GetThemeColor4fv(TH_NODE_FRAME, color); - const float alpha = color[3]; - - /* shadow */ - node_draw_shadow(snode, node, BASIS_RAD, alpha); - - /* body */ - if (node->flag & NODE_CUSTOM_COLOR) { - rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], alpha); - } - else { - UI_GetThemeColor4fv(TH_NODE_FRAME, color); - } - - const rctf *rct = &node->totr; - UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(rct, true, BASIS_RAD, color); - - /* outline active and selected emphasis */ - if (node->flag & SELECT) { - if (node->flag & NODE_ACTIVE) { - UI_GetThemeColorShadeAlpha4fv(TH_ACTIVE, 0, -40, color); - } - else { - UI_GetThemeColorShadeAlpha4fv(TH_SELECT, 0, -40, color); - } - - UI_draw_roundbox_aa(rct, false, BASIS_RAD, color); - } - - /* label */ - if (node->label[0] != '\0') { - node_draw_frame_label(ntree, node, snode->runtime->aspect); - } - - UI_block_end(C, node->block); - UI_block_draw(C, node->block); - node->block = NULL; -} - -static int node_resize_area_frame(bNode *node, int x, int y) -{ - const float size = 10.0f; - NodeFrame *data = (NodeFrame *)node->storage; - rctf totr = node->totr; - int dir = 0; - - /* shrinking frame size is determined by child nodes */ - if (!(data->flag & NODE_FRAME_RESIZEABLE)) { - return 0; - } - - if (x >= totr.xmax - size && x < totr.xmax && y >= totr.ymin && y < totr.ymax) { - dir |= NODE_RESIZE_RIGHT; - } - if (x >= totr.xmin && x < totr.xmin + size && y >= totr.ymin && y < totr.ymax) { - dir |= NODE_RESIZE_LEFT; - } - if (x >= totr.xmin && x < totr.xmax && y >= totr.ymax - size && y < totr.ymax) { - dir |= NODE_RESIZE_TOP; - } - if (x >= totr.xmin && x < totr.xmax && y >= totr.ymin && y < totr.ymin + size) { - dir |= NODE_RESIZE_BOTTOM; - } - - return dir; -} - -static void node_buts_frame_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "label_size", DEFAULT_FLAGS, IFACE_("Label Size"), ICON_NONE); - uiItemR(layout, ptr, "shrink", DEFAULT_FLAGS, IFACE_("Shrink"), ICON_NONE); - uiItemR(layout, ptr, "text", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -#define NODE_REROUTE_SIZE 8.0f - -static void node_draw_reroute_prepare(const bContext *UNUSED(C), - bNodeTree *UNUSED(ntree), - bNode *node) -{ - /* get "global" coords */ - float locx, locy; - node_to_view(node, 0.0f, 0.0f, &locx, &locy); - - /* reroute node has exactly one input and one output, both in the same place */ - bNodeSocket *nsock = node->outputs.first; - nsock->locx = locx; - nsock->locy = locy; - - nsock = node->inputs.first; - nsock->locx = locx; - nsock->locy = locy; - - const float size = NODE_REROUTE_SIZE; - node->width = size * 2; - node->totr.xmin = locx - size; - node->totr.xmax = locx + size; - node->totr.ymax = locy + size; - node->totr.ymin = locy - size; -} - -static void node_draw_reroute(const bContext *C, - ARegion *region, - SpaceNode *UNUSED(snode), - bNodeTree *ntree, - bNode *node, - bNodeInstanceKey UNUSED(key)) -{ - char showname[128]; /* 128 used below */ - rctf *rct = &node->totr; - - /* skip if out of view */ - if (node->totr.xmax < region->v2d.cur.xmin || node->totr.xmin > region->v2d.cur.xmax || - node->totr.ymax < region->v2d.cur.ymin || node->totr.ymin > region->v2d.cur.ymax) { - UI_block_end(C, node->block); - node->block = NULL; - return; - } - - /* XXX only kept for debugging - * selection state is indicated by socket outline below! - */ -#if 0 - float size = NODE_REROUTE_SIZE; - - /* body */ - float debug_color[4]; - UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_GetThemeColor4fv(TH_NODE, debug_color); - UI_draw_roundbox_aa(true, rct->xmin, rct->ymin, rct->xmax, rct->ymax, size, debug_color); - - /* outline active and selected emphasis */ - if (node->flag & SELECT) { - GPU_blend(GPU_BLEND_ALPHA); - GPU_line_smooth(true); - /* Using different shades of #TH_TEXT_HI for the emphasis, like triangle. */ - if (node->flag & NODE_ACTIVE) { - UI_GetThemeColorShadeAlpha4fv(TH_TEXT_HI, 0, -40, debug_color); - } - else { - UI_GetThemeColorShadeAlpha4fv(TH_TEXT_HI, -20, -120, debug_color); - } - UI_draw_roundbox_4fv(false, rct->xmin, rct->ymin, rct->xmax, rct->ymax, size, debug_color); - - GPU_line_smooth(false); - GPU_blend(GPU_BLEND_NONE); - } -#endif - - if (node->label[0] != '\0') { - /* draw title (node label) */ - BLI_strncpy(showname, node->label, sizeof(showname)); - uiDefBut(node->block, - UI_BTYPE_LABEL, - 0, - showname, - (int)(rct->xmin - NODE_DYS), - (int)(rct->ymax), - (short)512, - (short)NODE_DY, - NULL, - 0, - 0, - 0, - 0, - NULL); - } - - /* only draw input socket. as they all are placed on the same position. - * highlight also if node itself is selected, since we don't display the node body separately! - */ - node_draw_sockets(®ion->v2d, C, ntree, node, false, node->flag & SELECT); - - UI_block_end(C, node->block); - UI_block_draw(C, node->block); - node->block = NULL; -} - -/* Special tweak area for reroute node. - * Since this node is quite small, we use a larger tweak area for grabbing than for selection. - */ -static int node_tweak_area_reroute(bNode *node, int x, int y) -{ - /* square of tweak radius */ - const float tweak_radius_sq = square_f(24.0f); - - bNodeSocket *sock = node->inputs.first; - float dx = sock->locx - x; - float dy = sock->locy - y; - return (dx * dx + dy * dy <= tweak_radius_sq); -} - -static void node_common_set_butfunc(bNodeType *ntype) -{ - switch (ntype->type) { - case NODE_GROUP: - ntype->draw_buttons = node_draw_buttons_group; - break; - case NODE_FRAME: - ntype->draw_nodetype = node_draw_frame; - ntype->draw_nodetype_prepare = node_draw_frame_prepare; - ntype->draw_buttons_ex = node_buts_frame_ex; - ntype->resize_area_func = node_resize_area_frame; - break; - case NODE_REROUTE: - ntype->draw_nodetype = node_draw_reroute; - ntype->draw_nodetype_prepare = node_draw_reroute_prepare; - ntype->tweak_area_func = node_tweak_area_reroute; - break; - } -} - -/* ****************** BUTTON CALLBACKS FOR SHADER NODES ***************** */ - -static void node_buts_image_user(uiLayout *layout, - bContext *C, - PointerRNA *ptr, - PointerRNA *imaptr, - PointerRNA *iuserptr, - const bool show_layer_selection, - const bool show_color_management) -{ - if (!imaptr->data) { - return; - } - - uiLayout *col = uiLayoutColumn(layout, false); - - uiItemR(col, imaptr, "source", DEFAULT_FLAGS, "", ICON_NONE); - - const int source = RNA_enum_get(imaptr, "source"); - - if (source == IMA_SRC_SEQUENCE) { - /* don't use iuser->framenr directly - * because it may not be updated if auto-refresh is off */ - Scene *scene = CTX_data_scene(C); - ImageUser *iuser = iuserptr->data; - /* Image *ima = imaptr->data; */ /* UNUSED */ - - char numstr[32]; - const int framenr = BKE_image_user_frame_get(iuser, CFRA, NULL); - BLI_snprintf(numstr, sizeof(numstr), IFACE_("Frame: %d"), framenr); - uiItemL(layout, numstr, ICON_NONE); - } - - if (ELEM(source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "frame_duration", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "frame_start", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "frame_offset", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "use_cyclic", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "use_auto_refresh", DEFAULT_FLAGS, NULL, ICON_NONE); - } - - if (show_layer_selection && RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER && - RNA_boolean_get(ptr, "has_layers")) { - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "layer", DEFAULT_FLAGS, NULL, ICON_NONE); - } - - if (show_color_management) { - uiLayout *split = uiLayoutSplit(layout, 0.5f, true); - PointerRNA colorspace_settings_ptr = RNA_pointer_get(imaptr, "colorspace_settings"); - uiItemL(split, IFACE_("Color Space"), ICON_NONE); - uiItemR(split, &colorspace_settings_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE); - - /* Avoid losing changes image is painted. */ - if (BKE_image_is_dirty(imaptr->data)) { - uiLayoutSetEnabled(split, false); - } - } -} - -static void node_shader_buts_mapping(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_shader_buts_vector_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "rotation_type", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0); -} - -static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_vect_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(layout, ptr, "convert_from", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "convert_to", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "attribute_type", DEFAULT_FLAGS, IFACE_("Type"), ICON_NONE); - uiItemR(layout, ptr, "attribute_name", DEFAULT_FLAGS, IFACE_("Name"), ICON_NONE); -} - -static void node_shader_buts_wireframe(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "use_pixel_size", DEFAULT_FLAGS, NULL, 0); -} - -static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - PointerRNA imaptr = RNA_pointer_get(ptr, "image"); - PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); - - uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - uiTemplateID(layout, - C, - ptr, - "image", - "IMAGE_OT_new", - "IMAGE_OT_open", - NULL, - UI_TEMPLATE_ID_FILTER_ALL, - false, - NULL); - uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE); - - if (RNA_enum_get(ptr, "projection") == SHD_PROJ_BOX) { - uiItemR(layout, ptr, "projection_blend", DEFAULT_FLAGS, "Blend", ICON_NONE); - } - - uiItemR(layout, ptr, "extension", DEFAULT_FLAGS, "", ICON_NONE); - - /* note: image user properties used directly here, unlike compositor image node, - * which redefines them in the node struct RNA to get proper updates. - */ - node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr, false, true); -} - -static void node_shader_buts_tex_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); -} - -static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - PointerRNA imaptr = RNA_pointer_get(ptr, "image"); - PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); - - uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - uiTemplateID(layout, - C, - ptr, - "image", - "IMAGE_OT_new", - "IMAGE_OT_open", - NULL, - UI_TEMPLATE_ID_FILTER_ALL, - false, - NULL); - - uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE); - - node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr, false, true); -} - -static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); - - uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, IFACE_("Interpolation"), ICON_NONE); - uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, IFACE_("Projection"), ICON_NONE); -} - -static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "sky_type", DEFAULT_FLAGS, "", ICON_NONE); - - if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_PREETHAM) { - uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE); - } - if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_HOSEK) { - uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, NULL, ICON_NONE); - } - if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) { - uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, NULL, 0); - - uiLayout *col; - if (RNA_boolean_get(ptr, "sun_disc")) { - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, NULL, ICON_NONE); - } - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "air_density", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "dust_density", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "ozone_density", DEFAULT_FLAGS, NULL, ICON_NONE); - } -} - -static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "gradient_type", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_tex_magic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "turbulence_depth", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_shader_buts_tex_brick(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "offset", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, IFACE_("Offset"), ICON_NONE); - uiItemR(col, ptr, "offset_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "squash", DEFAULT_FLAGS, IFACE_("Squash"), ICON_NONE); - uiItemR(col, ptr, "squash_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); -} - -static void node_shader_buts_tex_wave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "wave_type", DEFAULT_FLAGS, "", ICON_NONE); - int type = RNA_enum_get(ptr, "wave_type"); - if (type == SHD_WAVE_BANDS) { - uiItemR(layout, ptr, "bands_direction", DEFAULT_FLAGS, "", ICON_NONE); - } - else { /* SHD_WAVE_RINGS */ - uiItemR(layout, ptr, "rings_direction", DEFAULT_FLAGS, "", ICON_NONE); - } - - uiItemR(layout, ptr, "wave_profile", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_tex_musgrave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "musgrave_dimensions", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "musgrave_type", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_tex_voronoi(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "voronoi_dimensions", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "feature", DEFAULT_FLAGS, "", ICON_NONE); - int feature = RNA_enum_get(ptr, "feature"); - if (!ELEM(feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS) && - RNA_enum_get(ptr, "voronoi_dimensions") != 1) { - uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, "", ICON_NONE); - } -} - -static void node_shader_buts_tex_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "noise_dimensions", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_tex_pointdensity(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - bNode *node = ptr->data; - NodeShaderTexPointDensity *shader_point_density = node->storage; - Object *ob = (Object *)node->id; - - PointerRNA ob_ptr, obdata_ptr; - RNA_id_pointer_create((ID *)ob, &ob_ptr); - RNA_id_pointer_create(ob ? (ID *)ob->data : NULL, &obdata_ptr); - - uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, ICON_NONE); - - if (node->id && shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { - PointerRNA dataptr; - RNA_id_pointer_create((ID *)node->id, &dataptr); - uiItemPointerR(layout, ptr, "particle_system", &dataptr, "particle_systems", NULL, ICON_NONE); - } - - uiItemR(layout, ptr, "space", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "radius", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "resolution", DEFAULT_FLAGS, NULL, ICON_NONE); - if (shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { - uiItemR(layout, ptr, "particle_color_source", DEFAULT_FLAGS, NULL, ICON_NONE); - } - else { - uiItemR(layout, ptr, "vertex_color_source", DEFAULT_FLAGS, NULL, ICON_NONE); - if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTWEIGHT) { - if (ob_ptr.data) { - uiItemPointerR( - layout, ptr, "vertex_attribute_name", &ob_ptr, "vertex_groups", "", ICON_NONE); - } - } - if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTCOL) { - if (obdata_ptr.data) { - uiItemPointerR( - layout, ptr, "vertex_attribute_name", &obdata_ptr, "vertex_colors", "", ICON_NONE); - } - } - } -} - -static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, 0); - uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0); -} - -static void node_shader_buts_bump(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0); -} - -static void node_shader_buts_uvmap(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0); - - if (!RNA_boolean_get(ptr, "from_instancer")) { - PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); - - if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { - PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); - uiItemPointerR(layout, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); - } - } -} - -static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); - if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { - PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); - - if (U.experimental.use_sculpt_vertex_colors && - RNA_collection_length(&dataptr, "sculpt_vertex_colors")) { - uiItemPointerR( - layout, ptr, "layer_name", &dataptr, "sculpt_vertex_colors", "", ICON_GROUP_VCOL); - } - else { - uiItemPointerR(layout, ptr, "layer_name", &dataptr, "vertex_colors", "", ICON_GROUP_VCOL); - } - } - else { - uiItemL(layout, "No mesh in active object.", ICON_ERROR); - } -} - -static void node_shader_buts_uvalongstroke(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "use_tips", DEFAULT_FLAGS, NULL, 0); -} - -static void node_shader_buts_normal_map(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", 0); - - if (RNA_enum_get(ptr, "space") == SHD_SPACE_TANGENT) { - PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); - - if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { - PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); - uiItemPointerR(layout, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); - } - else { - uiItemR(layout, ptr, "uv_map", DEFAULT_FLAGS, "", 0); - } - } -} - -static void node_shader_buts_displacement(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", 0); -} - -static void node_shader_buts_tangent(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - uiLayout *split, *row; - - split = uiLayoutSplit(layout, 0.0f, false); - - uiItemR(split, ptr, "direction_type", DEFAULT_FLAGS, "", 0); - - row = uiLayoutRow(split, false); - - if (RNA_enum_get(ptr, "direction_type") == SHD_TANGENT_UVMAP) { - PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); - - if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { - PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); - uiItemPointerR(row, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); - } - else { - uiItemR(row, ptr, "uv_map", DEFAULT_FLAGS, "", 0); - } - } - else { - uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, 0); - } -} - -static void node_shader_buts_glossy(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_principled(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "subsurface_method", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_anisotropic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_subsurface(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_toon(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "component", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_hair(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "component", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_principled_hair(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiItemR(layout, ptr, "parametrization", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *row; - - row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - - row = uiLayoutRow(layout, true); - - if (RNA_enum_get(ptr, "mode") == NODE_IES_INTERNAL) { - uiItemR(row, ptr, "ies", DEFAULT_FLAGS, "", ICON_NONE); - } - else { - uiItemR(row, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); - } -} - -static void node_shader_buts_script(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *row; - - row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - - row = uiLayoutRow(layout, true); - - if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_INTERNAL) { - uiItemR(row, ptr, "script", DEFAULT_FLAGS, "", ICON_NONE); - } - else { - uiItemR(row, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); - } - - uiItemO(row, "", ICON_FILE_REFRESH, "node.shader_script_update"); -} - -static void node_shader_buts_script_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - uiItemS(layout); - - node_shader_buts_script(layout, C, ptr); - -#if 0 /* not implemented yet */ - if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_EXTERNAL) { - uiItemR(layout, ptr, "use_auto_update", DEFAULT_FLAGS, NULL, ICON_NONE); - } -#endif -} - -static void node_buts_output_shader(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "target", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_buts_output_linestyle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *row, *col; - - col = uiLayoutColumn(layout, false); - row = uiLayoutRow(col, true); - uiItemR(row, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_shader_buts_bevel(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_shader_buts_ambient_occlusion(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "inside", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "only_local", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "noise_dimensions", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_shader_buts_output_aov(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "name", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -/* only once called */ -static void node_shader_set_butfunc(bNodeType *ntype) -{ - switch (ntype->type) { - case SH_NODE_NORMAL: - ntype->draw_buttons = node_buts_normal; - break; - case SH_NODE_CURVE_VEC: - ntype->draw_buttons = node_buts_curvevec; - break; - case SH_NODE_CURVE_RGB: - ntype->draw_buttons = node_buts_curvecol; - break; - case SH_NODE_MAPPING: - ntype->draw_buttons = node_shader_buts_mapping; - break; - case SH_NODE_VALUE: - ntype->draw_buttons = node_buts_value; - break; - case SH_NODE_RGB: - ntype->draw_buttons = node_buts_rgb; - break; - case SH_NODE_MIX_RGB: - ntype->draw_buttons = node_buts_mix_rgb; - break; - case SH_NODE_VALTORGB: - ntype->draw_buttons = node_buts_colorramp; - break; - case SH_NODE_CLAMP: - ntype->draw_buttons = node_shader_buts_clamp; - break; - case SH_NODE_MAP_RANGE: - ntype->draw_buttons = node_shader_buts_map_range; - break; - case SH_NODE_MATH: - ntype->draw_buttons = node_buts_math; - break; - case SH_NODE_VECTOR_MATH: - ntype->draw_buttons = node_shader_buts_vect_math; - break; - case SH_NODE_VECTOR_ROTATE: - ntype->draw_buttons = node_shader_buts_vector_rotate; - break; - case SH_NODE_VECT_TRANSFORM: - ntype->draw_buttons = node_shader_buts_vect_transform; - break; - case SH_NODE_ATTRIBUTE: - ntype->draw_buttons = node_shader_buts_attribute; - break; - case SH_NODE_WIREFRAME: - ntype->draw_buttons = node_shader_buts_wireframe; - break; - case SH_NODE_TEX_SKY: - ntype->draw_buttons = node_shader_buts_tex_sky; - break; - case SH_NODE_TEX_IMAGE: - ntype->draw_buttons = node_shader_buts_tex_image; - ntype->draw_buttons_ex = node_shader_buts_tex_image_ex; - break; - case SH_NODE_TEX_ENVIRONMENT: - ntype->draw_buttons = node_shader_buts_tex_environment; - ntype->draw_buttons_ex = node_shader_buts_tex_environment_ex; - break; - case SH_NODE_TEX_GRADIENT: - ntype->draw_buttons = node_shader_buts_tex_gradient; - break; - case SH_NODE_TEX_MAGIC: - ntype->draw_buttons = node_shader_buts_tex_magic; - break; - case SH_NODE_TEX_BRICK: - ntype->draw_buttons = node_shader_buts_tex_brick; - break; - case SH_NODE_TEX_WAVE: - ntype->draw_buttons = node_shader_buts_tex_wave; - break; - case SH_NODE_TEX_MUSGRAVE: - ntype->draw_buttons = node_shader_buts_tex_musgrave; - break; - case SH_NODE_TEX_VORONOI: - ntype->draw_buttons = node_shader_buts_tex_voronoi; - break; - case SH_NODE_TEX_NOISE: - ntype->draw_buttons = node_shader_buts_tex_noise; - break; - case SH_NODE_TEX_POINTDENSITY: - ntype->draw_buttons = node_shader_buts_tex_pointdensity; - break; - case SH_NODE_TEX_COORD: - ntype->draw_buttons = node_shader_buts_tex_coord; - break; - case SH_NODE_BUMP: - ntype->draw_buttons = node_shader_buts_bump; - break; - case SH_NODE_NORMAL_MAP: - ntype->draw_buttons = node_shader_buts_normal_map; - break; - case SH_NODE_DISPLACEMENT: - case SH_NODE_VECTOR_DISPLACEMENT: - ntype->draw_buttons = node_shader_buts_displacement; - break; - case SH_NODE_TANGENT: - ntype->draw_buttons = node_shader_buts_tangent; - break; - case SH_NODE_BSDF_GLOSSY: - case SH_NODE_BSDF_GLASS: - case SH_NODE_BSDF_REFRACTION: - ntype->draw_buttons = node_shader_buts_glossy; - break; - case SH_NODE_BSDF_PRINCIPLED: - ntype->draw_buttons = node_shader_buts_principled; - break; - case SH_NODE_BSDF_ANISOTROPIC: - ntype->draw_buttons = node_shader_buts_anisotropic; - break; - case SH_NODE_SUBSURFACE_SCATTERING: - ntype->draw_buttons = node_shader_buts_subsurface; - break; - case SH_NODE_BSDF_TOON: - ntype->draw_buttons = node_shader_buts_toon; - break; - case SH_NODE_BSDF_HAIR: - ntype->draw_buttons = node_shader_buts_hair; - break; - case SH_NODE_BSDF_HAIR_PRINCIPLED: - ntype->draw_buttons = node_shader_buts_principled_hair; - break; - case SH_NODE_SCRIPT: - ntype->draw_buttons = node_shader_buts_script; - ntype->draw_buttons_ex = node_shader_buts_script_ex; - break; - case SH_NODE_UVMAP: - ntype->draw_buttons = node_shader_buts_uvmap; - break; - case SH_NODE_VERTEX_COLOR: - ntype->draw_buttons = node_shader_buts_vertex_color; - break; - case SH_NODE_UVALONGSTROKE: - ntype->draw_buttons = node_shader_buts_uvalongstroke; - break; - case SH_NODE_OUTPUT_MATERIAL: - case SH_NODE_OUTPUT_LIGHT: - case SH_NODE_OUTPUT_WORLD: - ntype->draw_buttons = node_buts_output_shader; - break; - case SH_NODE_OUTPUT_LINESTYLE: - ntype->draw_buttons = node_buts_output_linestyle; - break; - case SH_NODE_TEX_IES: - ntype->draw_buttons = node_shader_buts_ies; - break; - case SH_NODE_BEVEL: - ntype->draw_buttons = node_shader_buts_bevel; - break; - case SH_NODE_AMBIENT_OCCLUSION: - ntype->draw_buttons = node_shader_buts_ambient_occlusion; - break; - case SH_NODE_TEX_WHITE_NOISE: - ntype->draw_buttons = node_shader_buts_white_noise; - break; - case SH_NODE_OUTPUT_AOV: - ntype->draw_buttons = node_shader_buts_output_aov; - break; - } -} - -/* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */ - -static void node_buts_image_views(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr, - PointerRNA *imaptr) -{ - uiLayout *col; - - if (!imaptr->data) { - return; - } - - col = uiLayoutColumn(layout, false); - - if (RNA_boolean_get(ptr, "has_views")) { - if (RNA_enum_get(ptr, "view") == 0) { - uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_CAMERA_STEREO); - } - else { - uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_SCENE); - } - } -} - -static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - - PointerRNA iuserptr; - RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); - uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - uiTemplateID(layout, - C, - ptr, - "image", - "IMAGE_OT_new", - "IMAGE_OT_open", - NULL, - UI_TEMPLATE_ID_FILTER_ALL, - false, - NULL); - if (!node->id) { - return; - } - - PointerRNA imaptr = RNA_pointer_get(ptr, "image"); - - node_buts_image_user(layout, C, ptr, &imaptr, &iuserptr, true, true); - - node_buts_image_views(layout, C, ptr, &imaptr); -} - -static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - - PointerRNA iuserptr; - RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); - uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 1); -} - -static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - uiLayout *col, *row; - - uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - - if (!node->id) { - return; - } - - col = uiLayoutColumn(layout, false); - row = uiLayoutRow(col, true); - uiItemR(row, ptr, "layer", DEFAULT_FLAGS, "", ICON_NONE); - - PropertyRNA *prop = RNA_struct_find_property(ptr, "layer"); - const char *layer_name; - if (!(RNA_property_enum_identifier( - C, ptr, prop, RNA_property_enum_get(ptr, prop), &layer_name))) { - return; - } - - PointerRNA scn_ptr; - char scene_name[MAX_ID_NAME - 2]; - scn_ptr = RNA_pointer_get(ptr, "scene"); - RNA_string_get(&scn_ptr, "name", scene_name); - - PointerRNA op_ptr; - uiItemFullO( - row, "RENDER_OT_render", "", ICON_RENDER_STILL, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); - RNA_string_set(&op_ptr, "layer", layer_name); - RNA_string_set(&op_ptr, "scene", scene_name); -} - -static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col, *row; - - col = uiLayoutColumn(layout, false); - const int filter = RNA_enum_get(ptr, "filter_type"); - const int reference = RNA_boolean_get(ptr, "use_variable_size"); - - uiItemR(col, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); - if (filter != R_FILTER_FAST_GAUSS) { - uiItemR(col, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE); - if (!reference) { - uiItemR(col, ptr, "use_bokeh", DEFAULT_FLAGS, NULL, ICON_NONE); - } - uiItemR(col, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE); - } - - uiItemR(col, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE); - - if (RNA_boolean_get(ptr, "use_relative")) { - uiItemL(col, IFACE_("Aspect Correction"), ICON_NONE); - row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "aspect_correction", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "factor_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); - uiItemR(col, ptr, "factor_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); - } - else { - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "size_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); - uiItemR(col, ptr, "size_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); - } - uiItemR(col, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_wrap", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, true); - uiItemL(col, IFACE_("Center:"), ICON_NONE); - uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); - uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); - - uiItemS(layout); - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiItemS(layout); - - uiItemR(layout, ptr, "spin", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "zoom", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_bilateralblur(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "sigma_color", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "sigma_space", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - uiLayout *sub, *col; - - col = uiLayoutColumn(layout, false); - uiItemL(col, IFACE_("Bokeh Type:"), ICON_NONE); - uiItemR(col, ptr, "bokeh", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiItemR(layout, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_zbuffer") == true); - uiItemR(col, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, NULL, ICON_NONE); - sub = uiLayoutColumn(col, false); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_zbuffer") == false); - uiItemR(sub, ptr, "z_scale", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, false); - - uiItemR(col, ptr, "threshold", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "contrast_limit", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "corner_rounding", 0, NULL, ICON_NONE); -} - -/* qdn: glare node */ -static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "glare_type", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "quality", DEFAULT_FLAGS, "", ICON_NONE); - - if (RNA_enum_get(ptr, "glare_type") != 1) { - uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE); - - if (RNA_enum_get(ptr, "glare_type") != 0) { - uiItemR(layout, ptr, "color_modulation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - } - } - - uiItemR(layout, ptr, "mix", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); - - if (RNA_enum_get(ptr, "glare_type") == 2) { - uiItemR(layout, ptr, "streaks", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "angle_offset", DEFAULT_FLAGS, NULL, ICON_NONE); - } - if (RNA_enum_get(ptr, "glare_type") == 0 || RNA_enum_get(ptr, "glare_type") == 2) { - uiItemR(layout, ptr, "fade", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - if (RNA_enum_get(ptr, "glare_type") == 0) { - uiItemR(layout, ptr, "use_rotate_45", DEFAULT_FLAGS, NULL, ICON_NONE); - } - } - if (RNA_enum_get(ptr, "glare_type") == 1) { - uiItemR(layout, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE); - } -} - -static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "tonemap_type", DEFAULT_FLAGS, "", ICON_NONE); - if (RNA_enum_get(ptr, "tonemap_type") == 0) { - uiItemR(col, ptr, "key", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE); - } - else { - uiItemR(col, ptr, "intensity", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "adaptation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "correction", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - } -} - -static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_projector", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(col, false); - uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_projector") == false); - uiItemR(col, ptr, "use_jitter", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "use_fit", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "factor", DEFAULT_FLAGS, IFACE_("Blur"), ICON_NONE); - - col = uiLayoutColumn(layout, true); - uiItemL(col, IFACE_("Speed:"), ICON_NONE); - uiItemR(col, ptr, "speed_min", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(col, ptr, "speed_max", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); - - uiItemR(layout, ptr, "use_curved", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "axis", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - uiItemR(layout, ptr, "use_crop_size", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "relative", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, true); - if (RNA_boolean_get(ptr, "relative")) { - uiItemR(col, ptr, "rel_min_x", DEFAULT_FLAGS, IFACE_("Left"), ICON_NONE); - uiItemR(col, ptr, "rel_max_x", DEFAULT_FLAGS, IFACE_("Right"), ICON_NONE); - uiItemR(col, ptr, "rel_min_y", DEFAULT_FLAGS, IFACE_("Up"), ICON_NONE); - uiItemR(col, ptr, "rel_max_y", DEFAULT_FLAGS, IFACE_("Down"), ICON_NONE); - } - else { - uiItemR(col, ptr, "min_x", DEFAULT_FLAGS, IFACE_("Left"), ICON_NONE); - uiItemR(col, ptr, "max_x", DEFAULT_FLAGS, IFACE_("Right"), ICON_NONE); - uiItemR(col, ptr, "min_y", DEFAULT_FLAGS, IFACE_("Up"), ICON_NONE); - uiItemR(col, ptr, "max_y", DEFAULT_FLAGS, IFACE_("Down"), ICON_NONE); - } -} - -static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *row, *col; - - col = uiLayoutColumn(layout, false); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(col, ptr, "factor", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_double_edge_mask(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, false); - - uiItemL(col, IFACE_("Inner Edge:"), ICON_NONE); - uiItemR(col, ptr, "inner_mode", DEFAULT_FLAGS, "", ICON_NONE); - uiItemL(col, IFACE_("Buffer Edge:"), ICON_NONE); - uiItemR(col, ptr, "edge_mode", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *sub, *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_min", DEFAULT_FLAGS, NULL, ICON_NONE); - sub = uiLayoutColumn(col, false); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_min")); - uiItemR(sub, ptr, "min", DEFAULT_FLAGS, "", ICON_NONE); - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_max", DEFAULT_FLAGS, NULL, ICON_NONE); - sub = uiLayoutColumn(col, false); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_max")); - uiItemR(sub, ptr, "max", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "premul", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "use_antialias_z", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE); - switch (RNA_enum_get(ptr, "mode")) { - case CMP_NODE_DILATEERODE_DISTANCE_THRESH: - uiItemR(layout, ptr, "edge", DEFAULT_FLAGS, NULL, ICON_NONE); - break; - case CMP_NODE_DILATEERODE_DISTANCE_FEATHER: - uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, NULL, ICON_NONE); - break; - } -} - -static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "threshold_neighbor", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - -static void node_composit_buts_distance_matte(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayout *col, *row; - - col = uiLayoutColumn(layout, true); - - uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); - row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - - uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - -static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *row, *col; - - uiItemL(layout, IFACE_("Despill Channel:"), ICON_NONE); - row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE); - - if (RNA_enum_get(ptr, "limit_method") == 0) { - uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - } - - uiItemR(col, ptr, "ratio", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "use_unspill", DEFAULT_FLAGS, NULL, ICON_NONE); - if (RNA_boolean_get(ptr, "use_unspill") == true) { - uiItemR(col, ptr, "unspill_red", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "unspill_green", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "unspill_blue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - } -} - -static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, true); - /*uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now */ - uiItemR(col, ptr, "gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - /*uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now*/ -} - -static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "color_hue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "color_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "color_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - -static void node_composit_buts_channel_matte(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayout *col, *row; - - uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); - row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "color_space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiItemL(col, IFACE_("Key Channel:"), ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "matte_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - - uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE); - if (RNA_enum_get(ptr, "limit_method") == 0) { - uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - } - - uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - -static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - -static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "alpha", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "index", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_antialiasing", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - PointerRNA imfptr = RNA_pointer_get(ptr, "format"); - const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; - - if (multilayer) { - uiItemL(layout, IFACE_("Path:"), ICON_NONE); - } - else { - uiItemL(layout, IFACE_("Base Path:"), ICON_NONE); - } - uiItemR(layout, ptr, "base_path", DEFAULT_FLAGS, "", ICON_NONE); -} -static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - Scene *scene = CTX_data_scene(C); - PointerRNA imfptr = RNA_pointer_get(ptr, "format"); - PointerRNA active_input_ptr, op_ptr; - uiLayout *row, *col; - const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; - const bool is_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR; - const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; - - node_composit_buts_file_output(layout, C, ptr); - uiTemplateImageSettings(layout, &imfptr, false); - - /* disable stereo output for multilayer, too much work for something that no one will use */ - /* if someone asks for that we can implement it */ - if (is_multiview) { - uiTemplateImageFormatViews(layout, &imfptr, NULL); - } - - uiItemS(layout); - - uiItemO(layout, IFACE_("Add Input"), ICON_ADD, "NODE_OT_output_file_add_socket"); - - row = uiLayoutRow(layout, false); - col = uiLayoutColumn(row, true); - - const int active_index = RNA_int_get(ptr, "active_input_index"); - /* using different collection properties if multilayer format is enabled */ - if (multilayer) { - uiTemplateList(col, - C, - "UI_UL_list", - "file_output_node", - ptr, - "layer_slots", - ptr, - "active_input_index", - NULL, - 0, - 0, - 0, - 0, - false, - false); - RNA_property_collection_lookup_int( - ptr, RNA_struct_find_property(ptr, "layer_slots"), active_index, &active_input_ptr); - } - else { - uiTemplateList(col, - C, - "UI_UL_list", - "file_output_node", - ptr, - "file_slots", - ptr, - "active_input_index", - NULL, - 0, - 0, - 0, - 0, - false, - false); - RNA_property_collection_lookup_int( - ptr, RNA_struct_find_property(ptr, "file_slots"), active_index, &active_input_ptr); - } - /* XXX collection lookup does not return the ID part of the pointer, - * setting this manually here */ - active_input_ptr.owner_id = ptr->owner_id; - - col = uiLayoutColumn(row, true); - wmOperatorType *ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false); - uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); - RNA_enum_set(&op_ptr, "direction", 1); - uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); - RNA_enum_set(&op_ptr, "direction", 2); - - if (active_input_ptr.data) { - if (multilayer) { - col = uiLayoutColumn(layout, true); - - uiItemL(col, IFACE_("Layer:"), ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &active_input_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE); - uiItemFullO(row, - "NODE_OT_output_file_remove_active_socket", - "", - ICON_X, - NULL, - WM_OP_EXEC_DEFAULT, - UI_ITEM_R_ICON_ONLY, - NULL); - } - else { - col = uiLayoutColumn(layout, true); - - uiItemL(col, IFACE_("File Subpath:"), ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &active_input_ptr, "path", DEFAULT_FLAGS, "", ICON_NONE); - uiItemFullO(row, - "NODE_OT_output_file_remove_active_socket", - "", - ICON_X, - NULL, - WM_OP_EXEC_DEFAULT, - UI_ITEM_R_ICON_ONLY, - NULL); - - /* format details for individual files */ - imfptr = RNA_pointer_get(&active_input_ptr, "format"); - - col = uiLayoutColumn(layout, true); - uiItemL(col, IFACE_("Format:"), ICON_NONE); - uiItemR(col, &active_input_ptr, "use_node_format", DEFAULT_FLAGS, NULL, ICON_NONE); - - const bool is_socket_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR; - const bool use_node_format = RNA_boolean_get(&active_input_ptr, "use_node_format"); - - if ((!is_exr && use_node_format) || (!is_socket_exr && !use_node_format)) { - uiItemR(col, &active_input_ptr, "save_as_render", DEFAULT_FLAGS, NULL, ICON_NONE); - } - - col = uiLayoutColumn(layout, false); - uiLayoutSetActive(col, use_node_format == false); - uiTemplateImageSettings(col, &imfptr, false); - - if (is_multiview) { - uiTemplateImageFormatViews(layout, &imfptr, NULL); - } - } - } -} - -static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", ICON_NONE); - - if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) { - uiLayout *row; - uiItemR(layout, ptr, "frame_method", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "offset_x", DEFAULT_FLAGS, "X", ICON_NONE); - uiItemR(row, ptr, "offset_y", DEFAULT_FLAGS, "Y", ICON_NONE); - } -} - -static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "invert_rgb", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "invert_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "mapping", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); -} - -static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *split, *col, *row; - - uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE); - - if (RNA_enum_get(ptr, "correction_method") == 0) { - - split = uiLayoutSplit(layout, 0.0f, false); - col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "lift", 1, 1, 0, 1); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "gamma", 1, 1, 1, 1); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "gain", 1, 1, 1, 1); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE); - } - else { - - split = uiLayoutSplit(layout, 0.0f, false); - col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "offset", 1, 1, 0, 1); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "offset_basis", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "power", 1, 1, 0, 1); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE); - - col = uiLayoutColumn(split, false); - uiTemplateColorPicker(col, ptr, "slope", 1, 1, 0, 1); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE); - } -} -static void node_composit_buts_colorbalance_ex(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE); - - if (RNA_enum_get(ptr, "correction_method") == 0) { - - uiTemplateColorPicker(layout, ptr, "lift", 1, 1, 0, 1); - uiItemR(layout, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiTemplateColorPicker(layout, ptr, "gamma", 1, 1, 1, 1); - uiItemR(layout, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiTemplateColorPicker(layout, ptr, "gain", 1, 1, 1, 1); - uiItemR(layout, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE); - } - else { - uiTemplateColorPicker(layout, ptr, "offset", 1, 1, 0, 1); - uiItemR(layout, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiTemplateColorPicker(layout, ptr, "power", 1, 1, 0, 1); - uiItemR(layout, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiTemplateColorPicker(layout, ptr, "slope", 1, 1, 0, 1); - uiItemR(layout, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE); - } -} - -static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - bNode *node = ptr->data; - CurveMapping *cumap = node->storage; - - if (_sample_col[0] != SAMPLE_FLT_ISNONE) { - cumap->flag |= CUMA_DRAW_SAMPLE; - copy_v3_v3(cumap->sample, _sample_col); - } - else { - cumap->flag &= ~CUMA_DRAW_SAMPLE; - } - - uiTemplateCurveMapping(layout, ptr, "mapping", 'h', false, false, false, false); -} - -static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); -} - -static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - PointerRNA clipptr; - - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - - if (!node->id) { - return; - } - - clipptr = RNA_pointer_get(ptr, "clip"); - - uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings"); -} - -static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - - if (!node->id) { - return; - } - - uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "wrap_axis", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - - if (!node->id) { - return; - } - - uiItemR(layout, ptr, "distortion_type", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_colorcorrection(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayout *row; - - row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE); - - row = uiLayoutRow(layout, false); - uiItemL(row, "", ICON_NONE); - uiItemL(row, IFACE_("Saturation"), ICON_NONE); - uiItemL(row, IFACE_("Contrast"), ICON_NONE); - uiItemL(row, IFACE_("Gamma"), ICON_NONE); - uiItemL(row, IFACE_("Gain"), ICON_NONE); - uiItemL(row, IFACE_("Lift"), ICON_NONE); - - row = uiLayoutRow(layout, false); - uiItemL(row, IFACE_("Master"), ICON_NONE); - uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - - row = uiLayoutRow(layout, false); - uiItemL(row, IFACE_("Highlights"), ICON_NONE); - uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - - row = uiLayoutRow(layout, false); - uiItemL(row, IFACE_("Midtones"), ICON_NONE); - uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - - row = uiLayoutRow(layout, false); - uiItemL(row, IFACE_("Shadows"), ICON_NONE); - uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); - - row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - -static void node_composit_buts_colorcorrection_ex(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayout *row; - - row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE); - row = layout; - uiItemL(row, IFACE_("Saturation"), ICON_NONE); - uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - uiItemL(row, IFACE_("Contrast"), ICON_NONE); - uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - uiItemL(row, IFACE_("Gamma"), ICON_NONE); - uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - uiItemL(row, IFACE_("Gain"), ICON_NONE); - uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - uiItemL(row, IFACE_("Lift"), ICON_NONE); - uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "check", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_switch_view_ex(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *UNUSED(ptr)) -{ - uiItemFullO(layout, - "NODE_OT_switch_view_update", - "Update Views", - ICON_FILE_REFRESH, - NULL, - WM_OP_INVOKE_DEFAULT, - 0, - NULL); -} - -static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *row; - - row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE); - - row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "flaps", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "rounding", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(layout, ptr, "catadioptric", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(layout, ptr, "shift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - -static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE); - // uiItemR(layout, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE); /* UNUSED */ - uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_backdrop_viewer( - SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) -{ - // node_composit_backdrop_canvas(snode, backdrop, node, x, y); - if (node->custom1 == 0) { - const float backdropWidth = backdrop->x; - const float backdropHeight = backdrop->y; - const float cx = x + snode->zoom * backdropWidth * node->custom3; - const float cy = y + snode->zoom * backdropHeight * node->custom4; - const float cross_size = 12 * U.pixelsize; - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor3f(1.0f, 1.0f, 1.0f); - - immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, cx - cross_size, cy - cross_size); - immVertex2f(pos, cx + cross_size, cy + cross_size); - immVertex2f(pos, cx + cross_size, cy - cross_size); - immVertex2f(pos, cx - cross_size, cy + cross_size); - immEnd(); - - immUnbindProgram(); - } -} - -static void node_composit_backdrop_boxmask( - SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) -{ - NodeBoxMask *boxmask = node->storage; - const float backdropWidth = backdrop->x; - const float backdropHeight = backdrop->y; - const float aspect = backdropWidth / backdropHeight; - const float rad = -boxmask->rotation; - const float cosine = cosf(rad); - const float sine = sinf(rad); - const float halveBoxWidth = backdropWidth * (boxmask->width / 2.0f); - const float halveBoxHeight = backdropHeight * (boxmask->height / 2.0f) * aspect; - - float cx, cy, x1, x2, x3, x4; - float y1, y2, y3, y4; - - cx = x + snode->zoom * backdropWidth * boxmask->x; - cy = y + snode->zoom * backdropHeight * boxmask->y; - - x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->zoom; - x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->zoom; - x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; - x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; - y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; - y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; - y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; - y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor3f(1.0f, 1.0f, 1.0f); - - immBegin(GPU_PRIM_LINE_LOOP, 4); - immVertex2f(pos, x1, y1); - immVertex2f(pos, x2, y2); - immVertex2f(pos, x3, y3); - immVertex2f(pos, x4, y4); - immEnd(); - - immUnbindProgram(); -} - -static void node_composit_backdrop_ellipsemask( - SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) -{ - NodeEllipseMask *ellipsemask = node->storage; - const float backdropWidth = backdrop->x; - const float backdropHeight = backdrop->y; - const float aspect = backdropWidth / backdropHeight; - const float rad = -ellipsemask->rotation; - const float cosine = cosf(rad); - const float sine = sinf(rad); - const float halveBoxWidth = backdropWidth * (ellipsemask->width / 2.0f); - const float halveBoxHeight = backdropHeight * (ellipsemask->height / 2.0f) * aspect; - - float cx, cy, x1, x2, x3, x4; - float y1, y2, y3, y4; - - cx = x + snode->zoom * backdropWidth * ellipsemask->x; - cy = y + snode->zoom * backdropHeight * ellipsemask->y; - - x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->zoom; - x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->zoom; - x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; - x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; - y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; - y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; - y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; - y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor3f(1.0f, 1.0f, 1.0f); - - immBegin(GPU_PRIM_LINE_LOOP, 4); - immVertex2f(pos, x1, y1); - immVertex2f(pos, x2, y2); - immVertex2f(pos, x3, y3); - immVertex2f(pos, x4, y4); - immEnd(); - - immUnbindProgram(); -} - -static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *row; - row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE); - row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_viewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "tile_order", DEFAULT_FLAGS, NULL, ICON_NONE); - if (RNA_enum_get(ptr, "tile_order") == 0) { - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, NULL, ICON_NONE); - } -} - -static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - - uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, NULL, ICON_NONE); - - uiItemR(layout, ptr, "size_source", DEFAULT_FLAGS, "", ICON_NONE); - - if (node->custom1 & (CMP_NODEFLAG_MASK_FIXED | CMP_NODEFLAG_MASK_FIXED_SCENE)) { - uiItemR(layout, ptr, "size_x", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "size_y", DEFAULT_FLAGS, NULL, ICON_NONE); - } - - uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE); - if (node->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) { - uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE); - } -} - -static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - - uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - - if (node->id) { - MovieClip *clip = (MovieClip *)node->id; - uiLayout *col; - PointerRNA tracking_ptr; - - RNA_pointer_create(&clip->id, &RNA_MovieTracking, &clip->tracking, &tracking_ptr); - - col = uiLayoutColumn(layout, true); - uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA); - } -} - -static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - /* bNode *node = ptr->data; */ /* UNUSED */ - - uiItemR(layout, ptr, "blur_pre", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "screen_balance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "despill_factor", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "despill_balance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "edge_kernel_radius", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "edge_kernel_tolerance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "clip_black", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "clip_white", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "dilate_distance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "feather_falloff", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "feather_distance", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "blur_post", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - - if (node->id) { - MovieClip *clip = (MovieClip *)node->id; - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *object; - uiLayout *col; - PointerRNA tracking_ptr; - NodeTrackPosData *data = node->storage; - - RNA_pointer_create(&clip->id, &RNA_MovieTracking, tracking, &tracking_ptr); - - col = uiLayoutColumn(layout, false); - uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA); - - object = BKE_tracking_object_get_named(tracking, data->tracking_object); - if (object) { - PointerRNA object_ptr; - - RNA_pointer_create(&clip->id, &RNA_MovieTrackingObject, object, &object_ptr); - - uiItemPointerR(col, ptr, "track_name", &object_ptr, "tracks", "", ICON_ANIM_DATA); - } - else { - uiItemR(layout, ptr, "track_name", DEFAULT_FLAGS, "", ICON_ANIM_DATA); - } - - uiItemR(layout, ptr, "position", DEFAULT_FLAGS, NULL, ICON_NONE); - - if (ELEM(node->custom1, CMP_TRACKPOS_RELATIVE_FRAME, CMP_TRACKPOS_ABSOLUTE_FRAME)) { - uiItemR(layout, ptr, "frame_relative", DEFAULT_FLAGS, NULL, ICON_NONE); - } - } -} - -static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - NodePlaneTrackDeformData *data = node->storage; - - uiTemplateID( - layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - - if (node->id) { - MovieClip *clip = (MovieClip *)node->id; - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *object; - uiLayout *col; - PointerRNA tracking_ptr; - - RNA_pointer_create(&clip->id, &RNA_MovieTracking, tracking, &tracking_ptr); - - col = uiLayoutColumn(layout, false); - uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA); - - object = BKE_tracking_object_get_named(tracking, data->tracking_object); - if (object) { - PointerRNA object_ptr; - - RNA_pointer_create(&clip->id, &RNA_MovieTrackingObject, object, &object_ptr); - - uiItemPointerR( - col, ptr, "plane_track_name", &object_ptr, "plane_tracks", "", ICON_ANIM_DATA); - } - else { - uiItemR(layout, ptr, "plane_track_name", 0, "", ICON_ANIM_DATA); - } - } - - uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE); - if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { - uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE); - } -} - -static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout), - bContext *UNUSED(C), - PointerRNA *UNUSED(ptr)) -{ -} - -static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, "", ICON_NONE); - uiItemR(layout, ptr, "ray_length", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - -static void node_composit_buts_cryptomatte_legacy(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayout *col = uiLayoutColumn(layout, true); - - uiItemL(col, IFACE_("Matte Objects:"), ICON_NONE); - - uiLayout *row = uiLayoutRow(col, true); - uiTemplateCryptoPicker(row, ptr, "add", ICON_ADD); - uiTemplateCryptoPicker(row, ptr, "remove", ICON_REMOVE); - - uiItemR(col, ptr, "matte_id", DEFAULT_FLAGS, "", ICON_NONE); -} - -static void node_composit_buts_cryptomatte_legacy_ex(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *UNUSED(ptr)) -{ - uiItemO(layout, IFACE_("Add Crypto Layer"), ICON_ADD, "NODE_OT_cryptomatte_layer_add"); - uiItemO(layout, IFACE_("Remove Crypto Layer"), ICON_REMOVE, "NODE_OT_cryptomatte_layer_remove"); -} - -static void node_composit_buts_cryptomatte(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - - uiLayout *row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - - uiLayout *col = uiLayoutColumn(layout, false); - if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { - uiTemplateID(col, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - } - else { - uiTemplateID( - col, C, ptr, "image", NULL, "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - - NodeCryptomatte *crypto = (NodeCryptomatte *)node->storage; - PointerRNA imaptr = RNA_pointer_get(ptr, "image"); - PointerRNA iuserptr; - RNA_pointer_create((ID *)ptr->owner_id, &RNA_ImageUser, &crypto->iuser, &iuserptr); - uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - - node_buts_image_user(col, C, ptr, &imaptr, &iuserptr, false, false); - node_buts_image_views(col, C, ptr, &imaptr); - } - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "layer_name", 0, "", ICON_NONE); - uiItemL(col, IFACE_("Matte ID:"), ICON_NONE); - - row = uiLayoutRow(col, true); - uiItemR(row, ptr, "matte_id", DEFAULT_FLAGS, "", ICON_NONE); - uiTemplateCryptoPicker(row, ptr, "add", ICON_ADD); - uiTemplateCryptoPicker(row, ptr, "remove", ICON_REMOVE); -} - -static void node_composit_buts_brightcontrast(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiItemR(layout, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ -#ifndef WITH_OPENIMAGEDENOISE - uiItemL(layout, IFACE_("Disabled, built without OpenImageDenoise"), ICON_ERROR); -#else - /* Always supported through Accelerate framework BNNS on macOS. */ -# ifndef __APPLE__ - if (!BLI_cpu_support_sse41()) { - uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR); - } -# endif -#endif - - uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, NULL, ICON_NONE); -} - -/* only once called */ -static void node_composit_set_butfunc(bNodeType *ntype) -{ - switch (ntype->type) { - case CMP_NODE_IMAGE: - ntype->draw_buttons = node_composit_buts_image; - ntype->draw_buttons_ex = node_composit_buts_image_ex; - break; - case CMP_NODE_R_LAYERS: - ntype->draw_buttons = node_composit_buts_viewlayers; - break; - case CMP_NODE_NORMAL: - ntype->draw_buttons = node_buts_normal; - break; - case CMP_NODE_CURVE_VEC: - ntype->draw_buttons = node_buts_curvevec; - break; - case CMP_NODE_CURVE_RGB: - ntype->draw_buttons = node_buts_curvecol; - break; - case CMP_NODE_VALUE: - ntype->draw_buttons = node_buts_value; - break; - case CMP_NODE_RGB: - ntype->draw_buttons = node_buts_rgb; - break; - case CMP_NODE_FLIP: - ntype->draw_buttons = node_composit_buts_flip; - break; - case CMP_NODE_SPLITVIEWER: - ntype->draw_buttons = node_composit_buts_splitviewer; - break; - case CMP_NODE_MIX_RGB: - ntype->draw_buttons = node_buts_mix_rgb; - break; - case CMP_NODE_VALTORGB: - ntype->draw_buttons = node_buts_colorramp; - break; - case CMP_NODE_CROP: - ntype->draw_buttons = node_composit_buts_crop; - break; - case CMP_NODE_BLUR: - ntype->draw_buttons = node_composit_buts_blur; - break; - case CMP_NODE_DBLUR: - ntype->draw_buttons = node_composit_buts_dblur; - break; - case CMP_NODE_BILATERALBLUR: - ntype->draw_buttons = node_composit_buts_bilateralblur; - break; - case CMP_NODE_DEFOCUS: - ntype->draw_buttons = node_composit_buts_defocus; - break; - case CMP_NODE_ANTIALIASING: - ntype->draw_buttons = node_composit_buts_antialiasing; - break; - case CMP_NODE_GLARE: - ntype->draw_buttons = node_composit_buts_glare; - break; - case CMP_NODE_TONEMAP: - ntype->draw_buttons = node_composit_buts_tonemap; - break; - case CMP_NODE_LENSDIST: - ntype->draw_buttons = node_composit_buts_lensdist; - break; - case CMP_NODE_VECBLUR: - ntype->draw_buttons = node_composit_buts_vecblur; - break; - case CMP_NODE_FILTER: - ntype->draw_buttons = node_composit_buts_filter; - break; - case CMP_NODE_MAP_VALUE: - ntype->draw_buttons = node_composit_buts_map_value; - break; - case CMP_NODE_MAP_RANGE: - ntype->draw_buttons = node_composit_buts_map_range; - break; - case CMP_NODE_TIME: - ntype->draw_buttons = node_buts_time; - break; - case CMP_NODE_ALPHAOVER: - ntype->draw_buttons = node_composit_buts_alphaover; - break; - case CMP_NODE_TEXTURE: - ntype->draw_buttons = node_buts_texture; - break; - case CMP_NODE_DILATEERODE: - ntype->draw_buttons = node_composit_buts_dilateerode; - break; - case CMP_NODE_INPAINT: - ntype->draw_buttons = node_composit_buts_inpaint; - break; - case CMP_NODE_DESPECKLE: - ntype->draw_buttons = node_composit_buts_despeckle; - break; - case CMP_NODE_OUTPUT_FILE: - ntype->draw_buttons = node_composit_buts_file_output; - ntype->draw_buttons_ex = node_composit_buts_file_output_ex; - break; - case CMP_NODE_DIFF_MATTE: - ntype->draw_buttons = node_composit_buts_diff_matte; - break; - case CMP_NODE_DIST_MATTE: - ntype->draw_buttons = node_composit_buts_distance_matte; - break; - case CMP_NODE_COLOR_SPILL: - ntype->draw_buttons = node_composit_buts_color_spill; - break; - case CMP_NODE_CHROMA_MATTE: - ntype->draw_buttons = node_composit_buts_chroma_matte; - break; - case CMP_NODE_COLOR_MATTE: - ntype->draw_buttons = node_composit_buts_color_matte; - break; - case CMP_NODE_SCALE: - ntype->draw_buttons = node_composit_buts_scale; - break; - case CMP_NODE_ROTATE: - ntype->draw_buttons = node_composit_buts_rotate; - break; - case CMP_NODE_CHANNEL_MATTE: - ntype->draw_buttons = node_composit_buts_channel_matte; - break; - case CMP_NODE_LUMA_MATTE: - ntype->draw_buttons = node_composit_buts_luma_matte; - break; - case CMP_NODE_MAP_UV: - ntype->draw_buttons = node_composit_buts_map_uv; - break; - case CMP_NODE_ID_MASK: - ntype->draw_buttons = node_composit_buts_id_mask; - break; - case CMP_NODE_DOUBLEEDGEMASK: - ntype->draw_buttons = node_composit_buts_double_edge_mask; - break; - case CMP_NODE_MATH: - ntype->draw_buttons = node_buts_math; - break; - case CMP_NODE_INVERT: - ntype->draw_buttons = node_composit_buts_invert; - break; - case CMP_NODE_PREMULKEY: - ntype->draw_buttons = node_composit_buts_premulkey; - break; - case CMP_NODE_VIEW_LEVELS: - ntype->draw_buttons = node_composit_buts_view_levels; - break; - case CMP_NODE_COLORBALANCE: - ntype->draw_buttons = node_composit_buts_colorbalance; - ntype->draw_buttons_ex = node_composit_buts_colorbalance_ex; - break; - case CMP_NODE_HUECORRECT: - ntype->draw_buttons = node_composit_buts_huecorrect; - break; - case CMP_NODE_ZCOMBINE: - ntype->draw_buttons = node_composit_buts_zcombine; - break; - case CMP_NODE_COMBYCCA: - case CMP_NODE_SEPYCCA: - ntype->draw_buttons = node_composit_buts_ycc; - break; - case CMP_NODE_MOVIECLIP: - ntype->draw_buttons = node_composit_buts_movieclip; - ntype->draw_buttons_ex = node_composit_buts_movieclip_ex; - break; - case CMP_NODE_STABILIZE2D: - ntype->draw_buttons = node_composit_buts_stabilize2d; - break; - case CMP_NODE_TRANSFORM: - ntype->draw_buttons = node_composit_buts_transform; - break; - case CMP_NODE_TRANSLATE: - ntype->draw_buttons = node_composit_buts_translate; - break; - case CMP_NODE_MOVIEDISTORTION: - ntype->draw_buttons = node_composit_buts_moviedistortion; - break; - case CMP_NODE_COLORCORRECTION: - ntype->draw_buttons = node_composit_buts_colorcorrection; - ntype->draw_buttons_ex = node_composit_buts_colorcorrection_ex; - break; - case CMP_NODE_SETALPHA: - ntype->draw_buttons = node_composit_buts_set_alpha; - break; - case CMP_NODE_SWITCH: - ntype->draw_buttons = node_composit_buts_switch; - break; - case CMP_NODE_SWITCH_VIEW: - ntype->draw_buttons_ex = node_composit_buts_switch_view_ex; - break; - case CMP_NODE_MASK_BOX: - ntype->draw_buttons = node_composit_buts_boxmask; - ntype->draw_backdrop = node_composit_backdrop_boxmask; - break; - case CMP_NODE_MASK_ELLIPSE: - ntype->draw_buttons = node_composit_buts_ellipsemask; - ntype->draw_backdrop = node_composit_backdrop_ellipsemask; - break; - case CMP_NODE_BOKEHIMAGE: - ntype->draw_buttons = node_composit_buts_bokehimage; - break; - case CMP_NODE_BOKEHBLUR: - ntype->draw_buttons = node_composit_buts_bokehblur; - break; - case CMP_NODE_VIEWER: - ntype->draw_buttons = node_composit_buts_viewer; - ntype->draw_buttons_ex = node_composit_buts_viewer_ex; - ntype->draw_backdrop = node_composit_backdrop_viewer; - break; - case CMP_NODE_COMPOSITE: - ntype->draw_buttons = node_composit_buts_composite; - break; - case CMP_NODE_MASK: - ntype->draw_buttons = node_composit_buts_mask; - break; - case CMP_NODE_KEYINGSCREEN: - ntype->draw_buttons = node_composit_buts_keyingscreen; - break; - case CMP_NODE_KEYING: - ntype->draw_buttons = node_composit_buts_keying; - break; - case CMP_NODE_TRACKPOS: - ntype->draw_buttons = node_composit_buts_trackpos; - break; - case CMP_NODE_PLANETRACKDEFORM: - ntype->draw_buttons = node_composit_buts_planetrackdeform; - break; - case CMP_NODE_CORNERPIN: - ntype->draw_buttons = node_composit_buts_cornerpin; - break; - case CMP_NODE_SUNBEAMS: - ntype->draw_buttons = node_composit_buts_sunbeams; - break; - case CMP_NODE_CRYPTOMATTE: - ntype->draw_buttons = node_composit_buts_cryptomatte; - break; - case CMP_NODE_CRYPTOMATTE_LEGACY: - ntype->draw_buttons = node_composit_buts_cryptomatte_legacy; - ntype->draw_buttons_ex = node_composit_buts_cryptomatte_legacy_ex; - break; - case CMP_NODE_BRIGHTCONTRAST: - ntype->draw_buttons = node_composit_buts_brightcontrast; - break; - case CMP_NODE_DENOISE: - ntype->draw_buttons = node_composit_buts_denoise; - break; - } -} - -/* ****************** BUTTON CALLBACKS FOR TEXTURE NODES ***************** */ - -static void node_texture_buts_bricks(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "offset", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, IFACE_("Offset"), ICON_NONE); - uiItemR(col, ptr, "offset_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "squash", DEFAULT_FLAGS, IFACE_("Squash"), ICON_NONE); - uiItemR(col, ptr, "squash_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); -} - -static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - PointerRNA tex_ptr; - bNode *node = ptr->data; - ID *id = ptr->owner_id; - Tex *tex = (Tex *)node->storage; - uiLayout *col, *row; - - RNA_pointer_create(id, &RNA_Texture, tex, &tex_ptr); - - col = uiLayoutColumn(layout, false); - - switch (tex->type) { - case TEX_BLEND: - uiItemR(col, &tex_ptr, "progression", DEFAULT_FLAGS, "", ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "use_flip_axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - break; - - case TEX_MARBLE: - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "marble_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - break; - - case TEX_MAGIC: - uiItemR(col, &tex_ptr, "noise_depth", DEFAULT_FLAGS, NULL, ICON_NONE); - break; - - case TEX_STUCCI: - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "stucci_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); - break; - - case TEX_WOOD: - uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(col, &tex_ptr, "wood_type", DEFAULT_FLAGS, "", ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - row = uiLayoutRow(col, false); - uiLayoutSetActive(row, !(ELEM(tex->stype, TEX_BAND, TEX_RING))); - uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - break; - - case TEX_CLOUDS: - uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "cloud_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(col, - &tex_ptr, - "noise_depth", - DEFAULT_FLAGS | UI_ITEM_R_EXPAND, - IFACE_("Depth"), - ICON_NONE); - break; - - case TEX_DISTNOISE: - uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(col, &tex_ptr, "noise_distortion", DEFAULT_FLAGS, "", ICON_NONE); - break; - - case TEX_MUSGRAVE: - uiItemR(col, &tex_ptr, "musgrave_type", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); - break; - case TEX_VORONOI: - uiItemR(col, &tex_ptr, "distance_metric", DEFAULT_FLAGS, "", ICON_NONE); - if (tex->vn_distm == TEX_MINKOVSKY) { - uiItemR(col, &tex_ptr, "minkovsky_exponent", DEFAULT_FLAGS, NULL, ICON_NONE); - } - uiItemR(col, &tex_ptr, "color_mode", DEFAULT_FLAGS, "", ICON_NONE); - break; - } -} - -static void node_texture_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - uiTemplateID(layout, - C, - ptr, - "image", - "IMAGE_OT_new", - "IMAGE_OT_open", - NULL, - UI_TEMPLATE_ID_FILTER_ALL, - false, - NULL); -} - -static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) -{ - bNode *node = ptr->data; - PointerRNA iuserptr; - - RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); -} - -static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); -} - -/* only once called */ -static void node_texture_set_butfunc(bNodeType *ntype) -{ - if (ntype->type >= TEX_NODE_PROC && ntype->type < TEX_NODE_PROC_MAX) { - ntype->draw_buttons = node_texture_buts_proc; - } - else { - switch (ntype->type) { - - case TEX_NODE_MATH: - ntype->draw_buttons = node_buts_math; - break; - - case TEX_NODE_MIX_RGB: - ntype->draw_buttons = node_buts_mix_rgb; - break; - - case TEX_NODE_VALTORGB: - ntype->draw_buttons = node_buts_colorramp; - break; - - case TEX_NODE_CURVE_RGB: - ntype->draw_buttons = node_buts_curvecol; - break; - - case TEX_NODE_CURVE_TIME: - ntype->draw_buttons = node_buts_time; - break; - - case TEX_NODE_TEXTURE: - ntype->draw_buttons = node_buts_texture; - break; - - case TEX_NODE_BRICKS: - ntype->draw_buttons = node_texture_buts_bricks; - break; - - case TEX_NODE_IMAGE: - ntype->draw_buttons = node_texture_buts_image; - ntype->draw_buttons_ex = node_texture_buts_image_ex; - break; - - case TEX_NODE_OUTPUT: - ntype->draw_buttons = node_texture_buts_output; - break; - } - } -} - -/* ****** init draw callbacks for all tree types, only called in usiblender.c, once ************ */ - -static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) -{ - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = ptr->data; - ED_node_tag_update_nodetree(bmain, ntree, node); -} - -static void node_socket_template_properties_update(bNodeType *ntype, bNodeSocketTemplate *stemp) -{ - StructRNA *srna = ntype->rna_ext.srna; - PropertyRNA *prop = RNA_struct_type_find_property(srna, stemp->identifier); - - if (prop) { - RNA_def_property_update_runtime(prop, node_property_update_default); - } -} - -static void node_template_properties_update(bNodeType *ntype) -{ - bNodeSocketTemplate *stemp; - - if (ntype->inputs) { - for (stemp = ntype->inputs; stemp->type >= 0; stemp++) { - node_socket_template_properties_update(ntype, stemp); - } - } - if (ntype->outputs) { - for (stemp = ntype->outputs; stemp->type >= 0; stemp++) { - node_socket_template_properties_update(ntype, stemp); - } - } -} - -static void node_socket_undefined_draw(bContext *UNUSED(C), - uiLayout *layout, - PointerRNA *UNUSED(ptr), - PointerRNA *UNUSED(node_ptr), - const char *UNUSED(text)) -{ - uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR); -} - -static void node_socket_undefined_draw_color(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), - PointerRNA *UNUSED(node_ptr), - float *r_color) -{ - r_color[0] = 1.0f; - r_color[1] = 0.0f; - r_color[2] = 0.0f; - r_color[3] = 1.0f; -} - -static void node_socket_undefined_interface_draw(bContext *UNUSED(C), - uiLayout *layout, - PointerRNA *UNUSED(ptr)) -{ - uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR); -} - -static void node_socket_undefined_interface_draw_color(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), - float *r_color) -{ - r_color[0] = 1.0f; - r_color[1] = 0.0f; - r_color[2] = 0.0f; - r_color[3] = 1.0f; -} - -void ED_node_init_butfuncs(void) -{ - /* Fallback types for undefined tree, nodes, sockets - * Defined in blenkernel, but not registered in type hashes. - */ - - /* default ui functions */ - NodeTypeUndefined.draw_nodetype = node_draw_default; - NodeTypeUndefined.draw_nodetype_prepare = node_update_default; - NodeTypeUndefined.select_area_func = node_select_area_default; - NodeTypeUndefined.tweak_area_func = node_tweak_area_default; - NodeTypeUndefined.draw_buttons = NULL; - NodeTypeUndefined.draw_buttons_ex = NULL; - NodeTypeUndefined.resize_area_func = node_resize_area_default; - - NodeSocketTypeUndefined.draw = node_socket_undefined_draw; - NodeSocketTypeUndefined.draw_color = node_socket_undefined_draw_color; - NodeSocketTypeUndefined.interface_draw = node_socket_undefined_interface_draw; - NodeSocketTypeUndefined.interface_draw_color = node_socket_undefined_interface_draw_color; - - /* node type ui functions */ - NODE_TYPES_BEGIN (ntype) { - /* default ui functions */ - ntype->draw_nodetype = node_draw_default; - ntype->draw_nodetype_prepare = node_update_default; - ntype->select_area_func = node_select_area_default; - ntype->tweak_area_func = node_tweak_area_default; - ntype->resize_area_func = node_resize_area_default; - - node_common_set_butfunc(ntype); - - node_composit_set_butfunc(ntype); - node_shader_set_butfunc(ntype); - node_texture_set_butfunc(ntype); - - /* define update callbacks for socket properties */ - node_template_properties_update(ntype); - } - NODE_TYPES_END; - - /* tree type icons */ - ntreeType_Composite->ui_icon = ICON_NODE_COMPOSITING; - ntreeType_Shader->ui_icon = ICON_NODE_MATERIAL; - ntreeType_Texture->ui_icon = ICON_NODE_TEXTURE; - ntreeType_Geometry->ui_icon = ICON_NODETREE; -} - -void ED_init_custom_node_type(bNodeType *ntype) -{ - /* default ui functions */ - ntype->draw_nodetype = node_draw_default; - ntype->draw_nodetype_prepare = node_update_default; - ntype->resize_area_func = node_resize_area_default; - ntype->select_area_func = node_select_area_default; - ntype->tweak_area_func = node_tweak_area_default; -} - -void ED_init_custom_node_socket_type(bNodeSocketType *stype) -{ - /* default ui functions */ - stype->draw = node_socket_button_label; -} - -static const float virtual_node_socket_color[4] = {0.2, 0.2, 0.2, 1.0}; - -/* maps standard socket integer type to a color */ -static const float std_node_socket_colors[][4] = { - {0.63, 0.63, 0.63, 1.0}, /* SOCK_FLOAT */ - {0.39, 0.39, 0.78, 1.0}, /* SOCK_VECTOR */ - {0.78, 0.78, 0.16, 1.0}, /* SOCK_RGBA */ - {0.39, 0.78, 0.39, 1.0}, /* SOCK_SHADER */ - {0.80, 0.65, 0.84, 1.0}, /* SOCK_BOOLEAN */ - {0.0, 0.0, 0.0, 1.0}, /*__SOCK_MESH (deprecated) */ - {0.35, 0.55, 0.36, 1.0}, /* SOCK_INT */ - {0.44, 0.70, 1.00, 1.0}, /* SOCK_STRING */ - {0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */ - {0.39, 0.22, 0.39, 1.0}, /* SOCK_IMAGE */ - {0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */ - {0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */ - {0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */ - {0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */ -}; - -/* common color callbacks for standard types */ -static void std_node_socket_draw_color(bContext *UNUSED(C), - PointerRNA *ptr, - PointerRNA *UNUSED(node_ptr), - float *r_color) -{ - bNodeSocket *sock = ptr->data; - int type = sock->typeinfo->type; - copy_v4_v4(r_color, std_node_socket_colors[type]); -} -static void std_node_socket_interface_draw_color(bContext *UNUSED(C), - PointerRNA *ptr, - float *r_color) -{ - bNodeSocket *sock = ptr->data; - int type = sock->typeinfo->type; - copy_v4_v4(r_color, std_node_socket_colors[type]); -} - -/* draw function for file output node sockets, - * displays only sub-path and format, no value button */ -static void node_file_output_socket_draw(bContext *C, - uiLayout *layout, - PointerRNA *ptr, - PointerRNA *node_ptr) -{ - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNodeSocket *sock = ptr->data; - uiLayout *row; - PointerRNA inputptr; - - row = uiLayoutRow(layout, false); - - PointerRNA imfptr = RNA_pointer_get(node_ptr, "format"); - int imtype = RNA_enum_get(&imfptr, "file_format"); - - if (imtype == R_IMF_IMTYPE_MULTILAYER) { - NodeImageMultiFileSocket *input = sock->storage; - RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr); - - uiItemL(row, input->layer, ICON_NONE); - } - else { - NodeImageMultiFileSocket *input = sock->storage; - uiBlock *block; - RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotFile, input, &inputptr); - - uiItemL(row, input->path, ICON_NONE); - - if (!RNA_boolean_get(&inputptr, "use_node_format")) { - imfptr = RNA_pointer_get(&inputptr, "format"); - } - - const char *imtype_name; - PropertyRNA *imtype_prop = RNA_struct_find_property(&imfptr, "file_format"); - RNA_property_enum_name((bContext *)C, - &imfptr, - imtype_prop, - RNA_property_enum_get(&imfptr, imtype_prop), - &imtype_name); - block = uiLayoutGetBlock(row); - UI_block_emboss_set(block, UI_EMBOSS_PULLDOWN); - uiItemL(row, imtype_name, ICON_NONE); - UI_block_emboss_set(block, UI_EMBOSS_NONE); - } -} - -static void std_node_socket_draw( - bContext *C, uiLayout *layout, PointerRNA *ptr, PointerRNA *node_ptr, const char *text) -{ - bNode *node = node_ptr->data; - bNodeSocket *sock = ptr->data; - int type = sock->typeinfo->type; - /*int subtype = sock->typeinfo->subtype;*/ - - /* XXX not nice, eventually give this node its own socket type ... */ - if (node->type == CMP_NODE_OUTPUT_FILE) { - node_file_output_socket_draw(C, layout, ptr, node_ptr); - return; - } - - if ((sock->in_out == SOCK_OUT) || (sock->flag & SOCK_IN_USE) || (sock->flag & SOCK_HIDE_VALUE)) { - node_socket_button_label(C, layout, ptr, node_ptr, text); - return; - } - - switch (type) { - case SOCK_FLOAT: - case SOCK_INT: - case SOCK_BOOLEAN: - uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); - break; - case SOCK_VECTOR: - if (sock->flag & SOCK_COMPACT) { - uiTemplateComponentMenu(layout, ptr, "default_value", text); - } - else { - if (sock->typeinfo->subtype == PROP_DIRECTION) { - uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE); - } - else { - uiLayout *column = uiLayoutColumn(layout, true); - uiItemR(column, ptr, "default_value", DEFAULT_FLAGS, text, ICON_NONE); - } - } - break; - case SOCK_RGBA: { - uiLayout *row = uiLayoutSplit(layout, 0.4f, false); - uiItemL(row, text, 0); - uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); - break; - } - case SOCK_STRING: { - uiLayout *row = uiLayoutSplit(layout, 0.4f, false); - uiItemL(row, text, 0); - - const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; - if (node_tree->type == NTREE_GEOMETRY) { - node_geometry_add_attribute_search_button(C, node_tree, node, ptr, row); - } - else { - uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); - } - - break; - } - case SOCK_OBJECT: { - uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); - break; - } - case SOCK_IMAGE: { - uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); - break; - } - case SOCK_COLLECTION: { - uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); - break; - } - case SOCK_TEXTURE: { - uiTemplateID(layout, C, ptr, "default_value", "texture.new", NULL, NULL, 0, ICON_NONE, NULL); - break; - } - case SOCK_MATERIAL: { - uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); - break; - } - default: - node_socket_button_label(C, layout, ptr, node_ptr, text); - break; - } -} - -static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout, PointerRNA *ptr) -{ - bNodeSocket *sock = ptr->data; - int type = sock->typeinfo->type; - - uiLayout *col = uiLayoutColumn(layout, false); - - switch (type) { - case SOCK_FLOAT: { - uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); - uiLayout *sub = uiLayoutColumn(col, true); - uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); - break; - } - case SOCK_INT: { - uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); - uiLayout *sub = uiLayoutColumn(col, true); - uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); - break; - } - case SOCK_VECTOR: { - uiItemR(col, ptr, "default_value", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE); - uiLayout *sub = uiLayoutColumn(col, true); - uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); - break; - } - case SOCK_BOOLEAN: - case SOCK_RGBA: - case SOCK_STRING: { - uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), 0); - break; - } - } - - uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, NULL, 0); -} - -void ED_init_standard_node_socket_type(bNodeSocketType *stype) -{ - stype->draw = std_node_socket_draw; - stype->draw_color = std_node_socket_draw_color; - stype->interface_draw = std_node_socket_interface_draw; - stype->interface_draw_color = std_node_socket_interface_draw_color; -} - -static void node_socket_virtual_draw_color(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), - PointerRNA *UNUSED(node_ptr), - float *r_color) -{ - copy_v4_v4(r_color, virtual_node_socket_color); -} - -void ED_init_node_socket_type_virtual(bNodeSocketType *stype) -{ - stype->draw = node_socket_button_label; - stype->draw_color = node_socket_virtual_draw_color; -} - -/* ************** Generic drawing ************** */ - -void draw_nodespace_back_pix(const bContext *C, - ARegion *region, - SpaceNode *snode, - bNodeInstanceKey parent_key) -{ - Main *bmain = CTX_data_main(C); - bNodeInstanceKey active_viewer_key = (snode->nodetree ? snode->nodetree->active_viewer_key : - NODE_INSTANCE_KEY_NONE); - GPU_matrix_push_projection(); - GPU_matrix_push(); - wmOrtho2_region_pixelspace(region); - GPU_matrix_identity_set(); - ED_region_draw_cb_draw(C, region, REGION_DRAW_BACKDROP); - GPU_matrix_pop_projection(); - GPU_matrix_pop(); - - if (!(snode->flag & SNODE_BACKDRAW) || !ED_node_is_compositor(snode)) { - return; - } - - if (parent_key.value != active_viewer_key.value) { - return; - } - - GPU_matrix_push_projection(); - GPU_matrix_push(); - - /* The draw manager is used to draw the backdrop image. */ - GPUFrameBuffer *old_fb = GPU_framebuffer_active_get(); - GPU_framebuffer_restore(); - BLI_thread_lock(LOCK_DRAW_IMAGE); - DRW_draw_view(C); - BLI_thread_unlock(LOCK_DRAW_IMAGE); - GPU_framebuffer_bind_no_srgb(old_fb); - /* Draw manager changes the depth state. Set it back to NONE. Without this the node preview - * images aren't drawn correctly. */ - GPU_depth_test(GPU_DEPTH_NONE); - - void *lock; - Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); - if (ibuf) { - /* somehow the offset has to be calculated inverse */ - wmOrtho2_region_pixelspace(region); - const float x = (region->winx - snode->zoom * ibuf->x) / 2 + snode->xof; - const float y = (region->winy - snode->zoom * ibuf->y) / 2 + snode->yof; - - /** \note draw selected info on backdrop */ - if (snode->edittree) { - bNode *node = snode->edittree->nodes.first; - rctf *viewer_border = &snode->nodetree->viewer_border; - while (node) { - if (node->flag & NODE_SELECT) { - if (node->typeinfo->draw_backdrop) { - node->typeinfo->draw_backdrop(snode, ibuf, node, x, y); - } - } - node = node->next; - } - - if ((snode->nodetree->flag & NTREE_VIEWER_BORDER) && - viewer_border->xmin < viewer_border->xmax && viewer_border->ymin < viewer_border->ymax) { - rcti pixel_border; - BLI_rcti_init(&pixel_border, - x + snode->zoom * viewer_border->xmin * ibuf->x, - x + snode->zoom * viewer_border->xmax * ibuf->x, - y + snode->zoom * viewer_border->ymin * ibuf->y, - y + snode->zoom * viewer_border->ymax * ibuf->y); - - uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColor(TH_ACTIVE); - - immDrawBorderCorners(pos, &pixel_border, 1.0f, 1.0f); - - immUnbindProgram(); - } - } - } - - BKE_image_release_ibuf(ima, ibuf, lock); - GPU_matrix_pop_projection(); - GPU_matrix_pop(); -} - -/* return quadratic beziers points for a given nodelink and clip if v2d is not NULL. */ -bool node_link_bezier_handles(const View2D *v2d, - const SpaceNode *snode, - const bNodeLink *link, - float vec[4][2]) -{ - float cursor[2] = {0.0f, 0.0f}; - - /* this function can be called with snode null (via cut_links_intersect) */ - /* XXX map snode->runtime->cursor back to view space */ - if (snode) { - cursor[0] = snode->runtime->cursor[0] * UI_DPI_FAC; - cursor[1] = snode->runtime->cursor[1] * UI_DPI_FAC; - } - - /* in v0 and v3 we put begin/end points */ - int toreroute, fromreroute; - if (link->fromsock) { - vec[0][0] = link->fromsock->locx; - vec[0][1] = link->fromsock->locy; - if (link->fromsock->flag & SOCK_MULTI_INPUT) { - node_link_calculate_multi_input_position(link->fromsock->locx, - link->fromsock->locy, - link->fromsock->total_inputs - 1, - link->fromsock->total_inputs, - vec[0]); - } - fromreroute = (link->fromnode && link->fromnode->type == NODE_REROUTE); - } - else { - if (snode == NULL) { - return false; - } - copy_v2_v2(vec[0], cursor); - fromreroute = 0; - } - if (link->tosock) { - vec[3][0] = link->tosock->locx; - vec[3][1] = link->tosock->locy; - if (!(link->tonode->flag & NODE_HIDDEN) && link->tosock->flag & SOCK_MULTI_INPUT) { - node_link_calculate_multi_input_position(link->tosock->locx, - link->tosock->locy, - link->multi_input_socket_index, - link->tosock->total_inputs, - vec[3]); - } - toreroute = (link->tonode && link->tonode->type == NODE_REROUTE); - } - else { - if (snode == NULL) { - return false; - } - copy_v2_v2(vec[3], cursor); - toreroute = 0; - } - - /* may be called outside of drawing (so pass spacetype) */ - int curving = UI_GetThemeValueType(TH_NODE_CURVING, SPACE_NODE); - - if (curving == 0) { - /* Straight line: align all points. */ - mid_v2_v2v2(vec[1], vec[0], vec[3]); - mid_v2_v2v2(vec[2], vec[1], vec[3]); - return true; - } - - const float dist = curving * 0.10f * fabsf(vec[0][0] - vec[3][0]); - const float deltax = vec[3][0] - vec[0][0]; - const float deltay = vec[3][1] - vec[0][1]; - /* check direction later, for top sockets */ - if (fromreroute) { - if (fabsf(deltax) > fabsf(deltay)) { - vec[1][1] = vec[0][1]; - vec[1][0] = vec[0][0] + (deltax > 0 ? dist : -dist); - } - else { - vec[1][0] = vec[0][0]; - vec[1][1] = vec[0][1] + (deltay > 0 ? dist : -dist); - } - } - else { - vec[1][0] = vec[0][0] + dist; - vec[1][1] = vec[0][1]; - } - if (toreroute) { - if (fabsf(deltax) > fabsf(deltay)) { - vec[2][1] = vec[3][1]; - vec[2][0] = vec[3][0] + (deltax > 0 ? -dist : dist); - } - else { - vec[2][0] = vec[3][0]; - vec[2][1] = vec[3][1] + (deltay > 0 ? -dist : dist); - } - } - else { - vec[2][0] = vec[3][0] - dist; - vec[2][1] = vec[3][1]; - } - - if (v2d && min_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) > v2d->cur.xmax) { - return false; /* clipped */ - } - if (v2d && max_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) < v2d->cur.xmin) { - return false; /* clipped */ - } - - return true; -} - -/* if v2d not NULL, it clips and returns 0 if not visible */ -bool node_link_bezier_points(const View2D *v2d, - const SpaceNode *snode, - const bNodeLink *link, - float coord_array[][2], - const int resol) -{ - float vec[4][2]; - - if (node_link_bezier_handles(v2d, snode, link, vec)) { - /* always do all three, to prevent data hanging around */ - BKE_curve_forward_diff_bezier( - vec[0][0], vec[1][0], vec[2][0], vec[3][0], coord_array[0] + 0, resol, sizeof(float[2])); - BKE_curve_forward_diff_bezier( - vec[0][1], vec[1][1], vec[2][1], vec[3][1], coord_array[0] + 1, resol, sizeof(float[2])); - - return true; - } - return false; -} - -#define NODELINK_GROUP_SIZE 256 -#define LINK_RESOL 24 -#define LINK_WIDTH (2.5f * UI_DPI_FAC) -#define ARROW_SIZE (7 * UI_DPI_FAC) - -/* Reroute arrow shape and mute bar. These are expanded here and shrunk in the glsl code. - * See: gpu_shader_2D_nodelink_vert.glsl */ -static float arrow_verts[3][2] = {{-1.0f, 1.0f}, {0.0f, 0.0f}, {-1.0f, -1.0f}}; -static float arrow_expand_axis[3][2] = {{0.7071f, 0.7071f}, {M_SQRT2, 0.0f}, {0.7071f, -0.7071f}}; -static float mute_verts[3][2] = {{0.7071f, 1.0f}, {0.7071f, 0.0f}, {0.7071f, -1.0f}}; -static float mute_expand_axis[3][2] = {{1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, -0.0f}}; - -static struct { - GPUBatch *batch; /* for batching line together */ - GPUBatch *batch_single; /* for single line */ - GPUVertBuf *inst_vbo; - uint p0_id, p1_id, p2_id, p3_id; - uint colid_id, muted_id; - GPUVertBufRaw p0_step, p1_step, p2_step, p3_step; - GPUVertBufRaw colid_step, muted_step; - uint count; - bool enabled; -} g_batch_link = {0}; - -static void nodelink_batch_reset(void) -{ - GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p0_id, &g_batch_link.p0_step); - GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p1_id, &g_batch_link.p1_step); - GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p2_id, &g_batch_link.p2_step); - GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p3_id, &g_batch_link.p3_step); - GPU_vertbuf_attr_get_raw_data( - g_batch_link.inst_vbo, g_batch_link.colid_id, &g_batch_link.colid_step); - GPU_vertbuf_attr_get_raw_data( - g_batch_link.inst_vbo, g_batch_link.muted_id, &g_batch_link.muted_step); - g_batch_link.count = 0; -} - -static void set_nodelink_vertex(GPUVertBuf *vbo, - uint uv_id, - uint pos_id, - uint exp_id, - uint v, - const uchar uv[2], - const float pos[2], - const float exp[2]) -{ - GPU_vertbuf_attr_set(vbo, uv_id, v, uv); - GPU_vertbuf_attr_set(vbo, pos_id, v, pos); - GPU_vertbuf_attr_set(vbo, exp_id, v, exp); -} - -static void nodelink_batch_init(void) -{ - GPUVertFormat format = {0}; - uint uv_id = GPU_vertformat_attr_add(&format, "uv", GPU_COMP_U8, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint expand_id = GPU_vertformat_attr_add(&format, "expand", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPUVertBuf *vbo = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_STATIC); - int vcount = LINK_RESOL * 2; /* curve */ - vcount += 2; /* restart strip */ - vcount += 3 * 2; /* arrow */ - vcount += 2; /* restart strip */ - vcount += 3 * 2; /* mute */ - vcount *= 2; /* shadow */ - vcount += 2; /* restart strip */ - GPU_vertbuf_data_alloc(vbo, vcount); - int v = 0; - - for (int k = 0; k < 2; k++) { - uchar uv[2] = {0, 0}; - float pos[2] = {0.0f, 0.0f}; - float exp[2] = {0.0f, 1.0f}; - - /* restart */ - if (k == 1) { - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - } - - /* curve strip */ - for (int i = 0; i < LINK_RESOL; i++) { - uv[0] = 255 * (i / (float)(LINK_RESOL - 1)); - uv[1] = 0; - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - uv[1] = 255; - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - } - /* restart */ - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - - uv[0] = 127; - uv[1] = 0; - copy_v2_v2(pos, arrow_verts[0]); - copy_v2_v2(exp, arrow_expand_axis[0]); - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - /* arrow */ - for (int i = 0; i < 3; i++) { - uv[1] = 0; - copy_v2_v2(pos, arrow_verts[i]); - copy_v2_v2(exp, arrow_expand_axis[i]); - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - - uv[1] = 255; - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - } - - /* restart */ - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - - uv[0] = 127; - uv[1] = 0; - copy_v2_v2(pos, mute_verts[0]); - copy_v2_v2(exp, mute_expand_axis[0]); - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - /* bar */ - for (int i = 0; i < 3; ++i) { - uv[1] = 0; - copy_v2_v2(pos, mute_verts[i]); - copy_v2_v2(exp, mute_expand_axis[i]); - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - - uv[1] = 255; - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - } - - /* restart */ - if (k == 0) { - set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); - } - } - - g_batch_link.batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); - gpu_batch_presets_register(g_batch_link.batch); - - g_batch_link.batch_single = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, 0); - gpu_batch_presets_register(g_batch_link.batch_single); - - /* Instances data */ - GPUVertFormat format_inst = {0}; - g_batch_link.p0_id = GPU_vertformat_attr_add( - &format_inst, "P0", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - g_batch_link.p1_id = GPU_vertformat_attr_add( - &format_inst, "P1", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - g_batch_link.p2_id = GPU_vertformat_attr_add( - &format_inst, "P2", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - g_batch_link.p3_id = GPU_vertformat_attr_add( - &format_inst, "P3", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - g_batch_link.colid_id = GPU_vertformat_attr_add( - &format_inst, "colid_doarrow", GPU_COMP_U8, 4, GPU_FETCH_INT); - g_batch_link.muted_id = GPU_vertformat_attr_add( - &format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT); - g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM); - /* Alloc max count but only draw the range we need. */ - GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE); - - GPU_batch_instbuf_set(g_batch_link.batch, g_batch_link.inst_vbo, true); - - nodelink_batch_reset(); -} - -static char nodelink_get_color_id(int th_col) -{ - switch (th_col) { - case TH_WIRE: - return 1; - case TH_WIRE_INNER: - return 2; - case TH_ACTIVE: - return 3; - case TH_EDGE_SELECT: - return 4; - case TH_REDALERT: - return 5; - } - return 0; -} - -static void nodelink_batch_draw(const SpaceNode *snode) -{ - if (g_batch_link.count == 0) { - return; - } - - GPU_blend(GPU_BLEND_ALPHA); - - float colors[6][4] = {{0.0f}}; - UI_GetThemeColor4fv(TH_WIRE_INNER, colors[nodelink_get_color_id(TH_WIRE_INNER)]); - UI_GetThemeColor4fv(TH_WIRE, colors[nodelink_get_color_id(TH_WIRE)]); - UI_GetThemeColor4fv(TH_ACTIVE, colors[nodelink_get_color_id(TH_ACTIVE)]); - UI_GetThemeColor4fv(TH_EDGE_SELECT, colors[nodelink_get_color_id(TH_EDGE_SELECT)]); - UI_GetThemeColor4fv(TH_REDALERT, colors[nodelink_get_color_id(TH_REDALERT)]); - - GPU_vertbuf_data_len_set(g_batch_link.inst_vbo, g_batch_link.count); - GPU_vertbuf_use(g_batch_link.inst_vbo); /* force update. */ - - GPU_batch_program_set_builtin(g_batch_link.batch, GPU_SHADER_2D_NODELINK_INST); - GPU_batch_uniform_4fv_array(g_batch_link.batch, "colors", 6, colors); - GPU_batch_uniform_1f(g_batch_link.batch, "expandSize", snode->runtime->aspect * LINK_WIDTH); - GPU_batch_uniform_1f(g_batch_link.batch, "arrowSize", ARROW_SIZE); - GPU_batch_draw(g_batch_link.batch); - - nodelink_batch_reset(); - - GPU_blend(GPU_BLEND_NONE); -} - -void nodelink_batch_start(SpaceNode *UNUSED(snode)) -{ - g_batch_link.enabled = true; -} - -void nodelink_batch_end(SpaceNode *snode) -{ - nodelink_batch_draw(snode); - g_batch_link.enabled = false; -} - -static void nodelink_batch_add_link(const SpaceNode *snode, - const float p0[2], - const float p1[2], - const float p2[2], - const float p3[2], - int th_col1, - int th_col2, - int th_col3, - bool drawarrow, - bool drawmuted) -{ - /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ - BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); - BLI_assert(ELEM(th_col2, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); - BLI_assert(ELEM(th_col3, TH_WIRE, TH_REDALERT, -1)); - - g_batch_link.count++; - copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0); - copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p1_step), p1); - copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p2_step), p2); - copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p3_step), p3); - char *colid = GPU_vertbuf_raw_step(&g_batch_link.colid_step); - colid[0] = nodelink_get_color_id(th_col1); - colid[1] = nodelink_get_color_id(th_col2); - colid[2] = nodelink_get_color_id(th_col3); - colid[3] = drawarrow; - char *muted = GPU_vertbuf_raw_step(&g_batch_link.muted_step); - muted[0] = drawmuted; - - if (g_batch_link.count == NODELINK_GROUP_SIZE) { - nodelink_batch_draw(snode); - } -} - -/* don't do shadows if th_col3 is -1. */ -void node_draw_link_bezier(const View2D *v2d, - const SpaceNode *snode, - const bNodeLink *link, - int th_col1, - int th_col2, - int th_col3) -{ - float vec[4][2]; - const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT; - if (node_link_bezier_handles(v2d, snode, link, vec)) { - int drawarrow = ((link->tonode && (link->tonode->type == NODE_REROUTE)) && - (link->fromnode && (link->fromnode->type == NODE_REROUTE))); - int drawmuted = (link->flag & NODE_LINK_MUTED); - if (g_batch_link.batch == NULL) { - nodelink_batch_init(); - } - - if (g_batch_link.enabled && !highlighted) { - /* Add link to batch. */ - nodelink_batch_add_link( - snode, vec[0], vec[1], vec[2], vec[3], th_col1, th_col2, th_col3, drawarrow, drawmuted); - } - else { - /* Draw single link. */ - float colors[3][4] = {{0.0f}}; - if (th_col3 != -1) { - UI_GetThemeColor4fv(th_col3, colors[0]); - } - UI_GetThemeColor4fv(th_col1, colors[1]); - UI_GetThemeColor4fv(th_col2, colors[2]); - - if (highlighted) { - float link_preselection_highlight_color[4]; - UI_GetThemeColor4fv(TH_SELECT, link_preselection_highlight_color); - copy_v4_v4(colors[2], link_preselection_highlight_color); - } - - GPUBatch *batch = g_batch_link.batch_single; - GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK); - GPU_batch_uniform_2fv_array(batch, "bezierPts", 4, vec); - GPU_batch_uniform_4fv_array(batch, "colors", 3, colors); - GPU_batch_uniform_1f(batch, "expandSize", snode->runtime->aspect * LINK_WIDTH); - GPU_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE); - GPU_batch_uniform_1i(batch, "doArrow", drawarrow); - GPU_batch_uniform_1i(batch, "doMuted", drawmuted); - GPU_batch_draw(batch); - } - } -} - -/* note; this is used for fake links in groups too */ -void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) -{ - int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE; - - if (link->fromsock == NULL && link->tosock == NULL) { - return; - } - - /* new connection */ - if (!link->fromsock || !link->tosock) { - th_col1 = th_col2 = TH_ACTIVE; - } - else { - /* going to give issues once... */ - if (link->tosock->flag & SOCK_UNAVAIL) { - return; - } - if (link->fromsock->flag & SOCK_UNAVAIL) { - return; - } - - if (link->flag & NODE_LINK_VALID) { - /* special indicated link, on drop-node */ - if (link->flag & NODE_LINKFLAG_HILITE) { - th_col1 = th_col2 = TH_ACTIVE; - } - else if (link->flag & NODE_LINK_MUTED) { - th_col1 = th_col2 = TH_REDALERT; - } - else { - /* Regular link, highlight if connected to selected node. */ - if (link->fromnode && link->fromnode->flag & SELECT) { - th_col1 = TH_EDGE_SELECT; - } - if (link->tonode && link->tonode->flag & SELECT) { - th_col2 = TH_EDGE_SELECT; - } - } - } - else { - /* Invalid link. */ - th_col1 = th_col2 = th_col3 = TH_REDALERT; - // th_col3 = -1; /* no shadow */ - } - } - - node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3); -} - -void ED_node_draw_snap(View2D *v2d, const float cent[2], float size, NodeBorder border, uint pos) -{ - immBegin(GPU_PRIM_LINES, 4); - - if (border & (NODE_LEFT | NODE_RIGHT)) { - immVertex2f(pos, cent[0], v2d->cur.ymin); - immVertex2f(pos, cent[0], v2d->cur.ymax); - } - else { - immVertex2f(pos, cent[0], cent[1] - size); - immVertex2f(pos, cent[0], cent[1] + size); - } - - if (border & (NODE_TOP | NODE_BOTTOM)) { - immVertex2f(pos, v2d->cur.xmin, cent[1]); - immVertex2f(pos, v2d->cur.xmax, cent[1]); - } - else { - immVertex2f(pos, cent[0] - size, cent[1]); - immVertex2f(pos, cent[0] + size, cent[1]); - } - - immEnd(); -} diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc new file mode 100644 index 00000000000..982af78dfde --- /dev/null +++ b/source/blender/editors/space_node/drawnode.cc @@ -0,0 +1,4261 @@ +/* + * 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. + */ + +/** \file + * \ingroup spnode + * \brief lower level node drawing for nodes (boarders, headers etc), also node layout. + */ + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_system.h" +#include "BLI_threads.h" + +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_text_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_tracking.h" + +#include "BLF_api.h" +#include "BLT_translation.h" + +#include "BIF_glutil.h" + +#include "GPU_batch.h" +#include "GPU_batch_presets.h" +#include "GPU_framebuffer.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "GPU_platform.h" +#include "GPU_state.h" + +#include "DRW_engine.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_node.h" +#include "ED_space_api.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf_types.h" + +#include "NOD_composite.h" +#include "NOD_geometry.h" +#include "NOD_shader.h" +#include "NOD_texture.h" +#include "node_intern.h" /* own include */ + +/* Default flags for uiItemR(). Name is kept short since this is used a lot in this file. */ +#define DEFAULT_FLAGS UI_ITEM_R_SPLIT_EMPTY_NAME + +/* ****************** SOCKET BUTTON DRAW FUNCTIONS ***************** */ + +static void node_socket_button_label(bContext *UNUSED(C), + uiLayout *layout, + PointerRNA *UNUSED(ptr), + PointerRNA *UNUSED(node_ptr), + const char *text) +{ + uiItemL(layout, text, 0); +} + +/* ****************** BUTTON CALLBACKS FOR ALL TREES ***************** */ + +static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + /* first output stores value */ + bNodeSocket *output = (bNodeSocket *)node->outputs.first; + PointerRNA sockptr; + RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); + + uiItemR(layout, &sockptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + /* first output stores value */ + bNodeSocket *output = (bNodeSocket *)node->outputs.first; + PointerRNA sockptr; + uiLayout *col; + RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); + + col = uiLayoutColumn(layout, false); + uiTemplateColorPicker(col, &sockptr, "default_value", true, false, false, false); + uiItemR(col, &sockptr, "default_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); +} + +static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + + uiLayout *col = uiLayoutColumn(layout, false); + uiLayout *row = uiLayoutRow(col, true); + uiItemR(row, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE); + if (ELEM(ntree->type, NTREE_COMPOSIT, NTREE_TEXTURE)) { + uiItemR(row, ptr, "use_alpha", DEFAULT_FLAGS, "", ICON_IMAGE_RGB_ALPHA); + } + + uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ +#if 0 + /* XXX no context access here .. */ + bNode *node = (bNode*)ptr->data; + CurveMapping *cumap = node->storage; + + if (cumap) { + cumap->flag |= CUMA_DRAW_CFRA; + if (node->custom1 < node->custom2) { + cumap->sample[0] = (float)(CFRA - node->custom1) / (float)(node->custom2 - node->custom1); + } + } +#endif + + uiTemplateCurveMapping(layout, ptr, "curve", 's', false, false, false, false); + + uiLayout *row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "frame_start", DEFAULT_FLAGS, IFACE_("Sta"), ICON_NONE); + uiItemR(row, ptr, "frame_end", DEFAULT_FLAGS, IFACE_("End"), ICON_NONE); +} + +static void node_buts_colorramp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiTemplateColorRamp(layout, ptr, "color_ramp", false); +} + +static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false); +} + +#define SAMPLE_FLT_ISNONE FLT_MAX +/* bad bad, 2.5 will do better?... no it won't... */ +static float _sample_col[4] = {SAMPLE_FLT_ISNONE}; +void ED_node_sample_set(const float col[4]) +{ + if (col) { + copy_v4_v4(_sample_col, col); + } + else { + copy_v4_fl(_sample_col, SAMPLE_FLT_ISNONE); + } +} + +static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + CurveMapping *cumap = (CurveMapping *)node->storage; + + if (_sample_col[0] != SAMPLE_FLT_ISNONE) { + cumap->flag |= CUMA_DRAW_SAMPLE; + copy_v3_v3(cumap->sample, _sample_col); + } + else { + cumap->flag &= ~CUMA_DRAW_SAMPLE; + } + + /* "Tone" (Standard/Film-like) only used in the Compositor. */ + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + uiTemplateCurveMapping( + layout, ptr, "mapping", 'c', false, false, false, (ntree->type == NTREE_COMPOSIT)); +} + +static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + /* first output stores normal */ + bNodeSocket *output = (bNodeSocket *)node->outputs.first; + PointerRNA sockptr; + RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); + + uiItemR(layout, &sockptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + short multi = (node->id && ((Tex *)node->id)->use_nodes && (node->type != CMP_NODE_TEXTURE) && + (node->type != TEX_NODE_TEXTURE)); + + uiItemR(layout, ptr, "texture", DEFAULT_FLAGS, "", ICON_NONE); + + if (multi) { + /* Number Drawing not optimal here, better have a list*/ + uiItemR(layout, ptr, "node_output", DEFAULT_FLAGS, "", ICON_NONE); + } +} + +static void node_shader_buts_clamp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "clamp_type", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "interpolation_type", DEFAULT_FLAGS, "", ICON_NONE); + if (!ELEM(RNA_enum_get(ptr, "interpolation_type"), + NODE_MAP_RANGE_SMOOTHSTEP, + NODE_MAP_RANGE_SMOOTHERSTEP)) { + uiItemR(layout, ptr, "clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); + } +} + +static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static int node_resize_area_default(bNode *node, int x, int y) +{ + if (node->flag & NODE_HIDDEN) { + rctf totr = node->totr; + /* right part of node */ + totr.xmin = node->totr.xmax - 20.0f; + if (BLI_rctf_isect_pt(&totr, x, y)) { + return NODE_RESIZE_RIGHT; + } + + return 0; + } + + const float size = NODE_RESIZE_MARGIN; + rctf totr = node->totr; + int dir = 0; + + if (x >= totr.xmax - size && x < totr.xmax && y >= totr.ymin && y < totr.ymax) { + dir |= NODE_RESIZE_RIGHT; + } + if (x >= totr.xmin && x < totr.xmin + size && y >= totr.ymin && y < totr.ymax) { + dir |= NODE_RESIZE_LEFT; + } + return dir; +} + +/* ****************** BUTTON CALLBACKS FOR COMMON NODES ***************** */ + +static void node_draw_buttons_group(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiTemplateIDBrowse( + layout, C, ptr, "node_tree", nullptr, nullptr, nullptr, UI_TEMPLATE_ID_FILTER_ALL, nullptr); +} + +/* XXX Does a bounding box update by iterating over all children. + * Not ideal to do this in every draw call, but doing as transform callback doesn't work, + * since the child node totr rects are not updated properly at that point. + */ +static void node_draw_frame_prepare(const bContext *UNUSED(C), bNodeTree *ntree, bNode *node) +{ + const float margin = 1.5f * U.widget_unit; + NodeFrame *data = (NodeFrame *)node->storage; + + /* init rect from current frame size */ + rctf rect; + node_to_view(node, node->offsetx, node->offsety, &rect.xmin, &rect.ymax); + node_to_view( + node, node->offsetx + node->width, node->offsety - node->height, &rect.xmax, &rect.ymin); + + /* frame can be resized manually only if shrinking is disabled or no children are attached */ + data->flag |= NODE_FRAME_RESIZEABLE; + /* for shrinking bbox, initialize the rect from first child node */ + bool bbinit = (data->flag & NODE_FRAME_SHRINK); + /* fit bounding box to all children */ + LISTBASE_FOREACH (bNode *, tnode, &ntree->nodes) { + if (tnode->parent != node) { + continue; + } + + /* add margin to node rect */ + rctf noderect = tnode->totr; + noderect.xmin -= margin; + noderect.xmax += margin; + noderect.ymin -= margin; + noderect.ymax += margin; + + /* first child initializes frame */ + if (bbinit) { + bbinit = false; + rect = noderect; + data->flag &= ~NODE_FRAME_RESIZEABLE; + } + else { + BLI_rctf_union(&rect, &noderect); + } + } + + /* now adjust the frame size from view-space bounding box */ + node_from_view(node, rect.xmin, rect.ymax, &node->offsetx, &node->offsety); + float xmax, ymax; + node_from_view(node, rect.xmax, rect.ymin, &xmax, &ymax); + node->width = xmax - node->offsetx; + node->height = -ymax + node->offsety; + + node->totr = rect; +} + +static void node_draw_frame_label(bNodeTree *ntree, bNode *node, const float aspect) +{ + /* XXX font id is crap design */ + const int fontid = UI_style_get()->widgetlabel.uifont_id; + NodeFrame *data = (NodeFrame *)node->storage; + const int font_size = data->label_size / aspect; + + char label[MAX_NAME]; + nodeLabel(ntree, node, label, sizeof(label)); + + BLF_enable(fontid, BLF_ASPECT); + BLF_aspect(fontid, aspect, aspect, 1.0f); + /* clamp otherwise it can suck up a LOT of memory */ + BLF_size(fontid, MIN2(24, font_size), U.dpi); + + /* title color */ + int color_id = node_get_colorid(node); + uchar color[3]; + UI_GetThemeColorBlendShade3ubv(TH_TEXT, color_id, 0.4f, 10, color); + BLF_color3ubv(fontid, color); + + const float margin = (float)(NODE_DY / 4); + const float width = BLF_width(fontid, label, sizeof(label)); + const float ascender = BLF_ascender(fontid); + const int label_height = ((margin / aspect) + (ascender * aspect)); + + /* 'x' doesn't need aspect correction */ + rctf *rct = &node->totr; + /* XXX a bit hacky, should use separate align values for x and y */ + 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); + + /* draw text body */ + if (node->id) { + Text *text = (Text *)node->id; + const int line_height_max = BLF_height_max(fontid); + const float line_spacing = (line_height_max * aspect); + const float line_width = (BLI_rctf_size_x(rct) - margin) / aspect; + + /* 'x' doesn't need aspect correction */ + x = rct->xmin + margin; + y = rct->ymax - (label_height + line_spacing); + /* early exit */ + int y_min = y + ((margin * 2) - (y - rct->ymin)); + + BLF_enable(fontid, BLF_CLIPPING | BLF_WORD_WRAP); + BLF_clipping(fontid, + rct->xmin, + /* round to avoid clipping half-way through a line */ + y - (floorf(((y - rct->ymin) - (margin * 2)) / line_spacing) * line_spacing), + rct->xmin + line_width, + rct->ymax); + + BLF_wordwrap(fontid, line_width); + + LISTBASE_FOREACH (TextLine *, line, &text->lines) { + struct ResultBLF info; + if (line->line[0]) { + BLF_position(fontid, x, y, 0); + BLF_draw_ex(fontid, line->line, line->len, &info); + y -= line_spacing * info.lines; + } + else { + y -= line_spacing; + } + if (y < y_min) { + break; + } + } + + BLF_disable(fontid, BLF_CLIPPING | BLF_WORD_WRAP); + } + + BLF_disable(fontid, BLF_ASPECT); +} + +static void node_draw_frame(const bContext *C, + ARegion *region, + SpaceNode *snode, + bNodeTree *ntree, + bNode *node, + bNodeInstanceKey UNUSED(key)) +{ + + /* skip if out of view */ + if (BLI_rctf_isect(&node->totr, ®ion->v2d.cur, nullptr) == false) { + UI_block_end(C, node->block); + node->block = nullptr; + return; + } + + float color[4]; + UI_GetThemeColor4fv(TH_NODE_FRAME, color); + const float alpha = color[3]; + + /* shadow */ + node_draw_shadow(snode, node, BASIS_RAD, alpha); + + /* body */ + if (node->flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], alpha); + } + else { + UI_GetThemeColor4fv(TH_NODE_FRAME, color); + } + + const rctf *rct = &node->totr; + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_aa(rct, true, BASIS_RAD, color); + + /* outline active and selected emphasis */ + if (node->flag & SELECT) { + if (node->flag & NODE_ACTIVE) { + UI_GetThemeColorShadeAlpha4fv(TH_ACTIVE, 0, -40, color); + } + else { + UI_GetThemeColorShadeAlpha4fv(TH_SELECT, 0, -40, color); + } + + UI_draw_roundbox_aa(rct, false, BASIS_RAD, color); + } + + /* label */ + if (node->label[0] != '\0') { + node_draw_frame_label(ntree, node, snode->runtime->aspect); + } + + UI_block_end(C, node->block); + UI_block_draw(C, node->block); + node->block = nullptr; +} + +static int node_resize_area_frame(bNode *node, int x, int y) +{ + const float size = 10.0f; + NodeFrame *data = (NodeFrame *)node->storage; + rctf totr = node->totr; + int dir = 0; + + /* shrinking frame size is determined by child nodes */ + if (!(data->flag & NODE_FRAME_RESIZEABLE)) { + return 0; + } + + if (x >= totr.xmax - size && x < totr.xmax && y >= totr.ymin && y < totr.ymax) { + dir |= NODE_RESIZE_RIGHT; + } + if (x >= totr.xmin && x < totr.xmin + size && y >= totr.ymin && y < totr.ymax) { + dir |= NODE_RESIZE_LEFT; + } + if (x >= totr.xmin && x < totr.xmax && y >= totr.ymax - size && y < totr.ymax) { + dir |= NODE_RESIZE_TOP; + } + if (x >= totr.xmin && x < totr.xmax && y >= totr.ymin && y < totr.ymin + size) { + dir |= NODE_RESIZE_BOTTOM; + } + + return dir; +} + +static void node_buts_frame_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "label_size", DEFAULT_FLAGS, IFACE_("Label Size"), ICON_NONE); + uiItemR(layout, ptr, "shrink", DEFAULT_FLAGS, IFACE_("Shrink"), ICON_NONE); + uiItemR(layout, ptr, "text", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +#define NODE_REROUTE_SIZE 8.0f + +static void node_draw_reroute_prepare(const bContext *UNUSED(C), + bNodeTree *UNUSED(ntree), + bNode *node) +{ + /* get "global" coords */ + float locx, locy; + node_to_view(node, 0.0f, 0.0f, &locx, &locy); + + /* reroute node has exactly one input and one output, both in the same place */ + bNodeSocket *nsock = (bNodeSocket *)node->outputs.first; + nsock->locx = locx; + nsock->locy = locy; + + nsock = (bNodeSocket *)node->inputs.first; + nsock->locx = locx; + nsock->locy = locy; + + const float size = NODE_REROUTE_SIZE; + node->width = size * 2; + node->totr.xmin = locx - size; + node->totr.xmax = locx + size; + node->totr.ymax = locy + size; + node->totr.ymin = locy - size; +} + +static void node_draw_reroute(const bContext *C, + ARegion *region, + SpaceNode *UNUSED(snode), + bNodeTree *ntree, + bNode *node, + bNodeInstanceKey UNUSED(key)) +{ + char showname[128]; /* 128 used below */ + rctf *rct = &node->totr; + + /* skip if out of view */ + if (node->totr.xmax < region->v2d.cur.xmin || node->totr.xmin > region->v2d.cur.xmax || + node->totr.ymax < region->v2d.cur.ymin || node->totr.ymin > region->v2d.cur.ymax) { + UI_block_end(C, node->block); + node->block = nullptr; + return; + } + + /* XXX only kept for debugging + * selection state is indicated by socket outline below! + */ +#if 0 + float size = NODE_REROUTE_SIZE; + + /* body */ + float debug_color[4]; + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_GetThemeColor4fv(TH_NODE, debug_color); + UI_draw_roundbox_aa(true, rct->xmin, rct->ymin, rct->xmax, rct->ymax, size, debug_color); + + /* outline active and selected emphasis */ + if (node->flag & SELECT) { + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_smooth(true); + /* Using different shades of #TH_TEXT_HI for the emphasis, like triangle. */ + if (node->flag & NODE_ACTIVE) { + UI_GetThemeColorShadeAlpha4fv(TH_TEXT_HI, 0, -40, debug_color); + } + else { + UI_GetThemeColorShadeAlpha4fv(TH_TEXT_HI, -20, -120, debug_color); + } + UI_draw_roundbox_4fv(false, rct->xmin, rct->ymin, rct->xmax, rct->ymax, size, debug_color); + + GPU_line_smooth(false); + GPU_blend(GPU_BLEND_NONE); + } +#endif + + if (node->label[0] != '\0') { + /* draw title (node label) */ + BLI_strncpy(showname, node->label, sizeof(showname)); + uiDefBut(node->block, + UI_BTYPE_LABEL, + 0, + showname, + (int)(rct->xmin - NODE_DYS), + (int)(rct->ymax), + (short)512, + (short)NODE_DY, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + } + + /* only draw input socket. as they all are placed on the same position. + * highlight also if node itself is selected, since we don't display the node body separately! + */ + node_draw_sockets(®ion->v2d, C, ntree, node, false, node->flag & SELECT); + + UI_block_end(C, node->block); + UI_block_draw(C, node->block); + node->block = nullptr; +} + +/* Special tweak area for reroute node. + * Since this node is quite small, we use a larger tweak area for grabbing than for selection. + */ +static int node_tweak_area_reroute(bNode *node, int x, int y) +{ + /* square of tweak radius */ + const float tweak_radius_sq = square_f(24.0f); + + bNodeSocket *sock = (bNodeSocket *)node->inputs.first; + float dx = sock->locx - x; + float dy = sock->locy - y; + return (dx * dx + dy * dy <= tweak_radius_sq); +} + +static void node_common_set_butfunc(bNodeType *ntype) +{ + switch (ntype->type) { + case NODE_GROUP: + ntype->draw_buttons = node_draw_buttons_group; + break; + case NODE_FRAME: + ntype->draw_nodetype = node_draw_frame; + ntype->draw_nodetype_prepare = node_draw_frame_prepare; + ntype->draw_buttons_ex = node_buts_frame_ex; + ntype->resize_area_func = node_resize_area_frame; + break; + case NODE_REROUTE: + ntype->draw_nodetype = node_draw_reroute; + ntype->draw_nodetype_prepare = node_draw_reroute_prepare; + ntype->tweak_area_func = node_tweak_area_reroute; + break; + } +} + +/* ****************** BUTTON CALLBACKS FOR SHADER NODES ***************** */ + +static void node_buts_image_user(uiLayout *layout, + bContext *C, + PointerRNA *ptr, + PointerRNA *imaptr, + PointerRNA *iuserptr, + const bool show_layer_selection, + const bool show_color_management) +{ + if (!imaptr->data) { + return; + } + + uiLayout *col = uiLayoutColumn(layout, false); + + uiItemR(col, imaptr, "source", DEFAULT_FLAGS, "", ICON_NONE); + + const int source = RNA_enum_get(imaptr, "source"); + + if (source == IMA_SRC_SEQUENCE) { + /* don't use iuser->framenr directly + * because it may not be updated if auto-refresh is off */ + Scene *scene = CTX_data_scene(C); + ImageUser *iuser = (ImageUser *)iuserptr->data; + /* Image *ima = imaptr->data; */ /* UNUSED */ + + char numstr[32]; + const int framenr = BKE_image_user_frame_get(iuser, CFRA, nullptr); + BLI_snprintf(numstr, sizeof(numstr), IFACE_("Frame: %d"), framenr); + uiItemL(layout, numstr, ICON_NONE); + } + + if (ELEM(source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "frame_duration", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "frame_start", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "frame_offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_cyclic", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_auto_refresh", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + + if (show_layer_selection && RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER && + RNA_boolean_get(ptr, "has_layers")) { + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "layer", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + + if (show_color_management) { + uiLayout *split = uiLayoutSplit(layout, 0.5f, true); + PointerRNA colorspace_settings_ptr = RNA_pointer_get(imaptr, "colorspace_settings"); + uiItemL(split, IFACE_("Color Space"), ICON_NONE); + uiItemR(split, &colorspace_settings_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE); + + /* Avoid losing changes image is painted. */ + if (BKE_image_is_dirty((Image *)imaptr->data)) { + uiLayoutSetEnabled(split, false); + } + } +} + +static void node_shader_buts_mapping(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_shader_buts_vector_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "rotation_type", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, 0); +} + +static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_vect_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "convert_from", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "convert_to", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "attribute_type", DEFAULT_FLAGS, IFACE_("Type"), ICON_NONE); + uiItemR(layout, ptr, "attribute_name", DEFAULT_FLAGS, IFACE_("Name"), ICON_NONE); +} + +static void node_shader_buts_wireframe(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_pixel_size", DEFAULT_FLAGS, nullptr, 0); +} + +static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + PointerRNA imaptr = RNA_pointer_get(ptr, "image"); + PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); + + uiLayoutSetContextPointer(layout, "image_user", &iuserptr); + uiTemplateID(layout, + C, + ptr, + "image", + "IMAGE_OT_new", + "IMAGE_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE); + + if (RNA_enum_get(ptr, "projection") == SHD_PROJ_BOX) { + uiItemR(layout, ptr, "projection_blend", DEFAULT_FLAGS, "Blend", ICON_NONE); + } + + uiItemR(layout, ptr, "extension", DEFAULT_FLAGS, "", ICON_NONE); + + /* note: image user properties used directly here, unlike compositor image node, + * which redefines them in the node struct RNA to get proper updates. + */ + node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr, false, true); +} + +static void node_shader_buts_tex_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false); +} + +static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + PointerRNA imaptr = RNA_pointer_get(ptr, "image"); + PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); + + uiLayoutSetContextPointer(layout, "image_user", &iuserptr); + uiTemplateID(layout, + C, + ptr, + "image", + "IMAGE_OT_new", + "IMAGE_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE); + + node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr, false, true); +} + +static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false); + + uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, IFACE_("Interpolation"), ICON_NONE); + uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, IFACE_("Projection"), ICON_NONE); +} + +static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "sky_type", DEFAULT_FLAGS, "", ICON_NONE); + + if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_PREETHAM) { + uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_HOSEK) { + uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) { + uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, nullptr, 0); + + uiLayout *col; + if (RNA_boolean_get(ptr, "sun_disc")) { + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "air_density", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "dust_density", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "ozone_density", DEFAULT_FLAGS, nullptr, ICON_NONE); + } +} + +static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "gradient_type", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_tex_magic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "turbulence_depth", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_shader_buts_tex_brick(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, IFACE_("Offset"), ICON_NONE); + uiItemR(col, ptr, "offset_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "squash", DEFAULT_FLAGS, IFACE_("Squash"), ICON_NONE); + uiItemR(col, ptr, "squash_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); +} + +static void node_shader_buts_tex_wave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "wave_type", DEFAULT_FLAGS, "", ICON_NONE); + int type = RNA_enum_get(ptr, "wave_type"); + if (type == SHD_WAVE_BANDS) { + uiItemR(layout, ptr, "bands_direction", DEFAULT_FLAGS, "", ICON_NONE); + } + else { /* SHD_WAVE_RINGS */ + uiItemR(layout, ptr, "rings_direction", DEFAULT_FLAGS, "", ICON_NONE); + } + + uiItemR(layout, ptr, "wave_profile", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_tex_musgrave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "musgrave_dimensions", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "musgrave_type", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_tex_voronoi(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "voronoi_dimensions", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "feature", DEFAULT_FLAGS, "", ICON_NONE); + int feature = RNA_enum_get(ptr, "feature"); + if (!ELEM(feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS) && + RNA_enum_get(ptr, "voronoi_dimensions") != 1) { + uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, "", ICON_NONE); + } +} + +static void node_shader_buts_tex_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "noise_dimensions", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_tex_pointdensity(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + NodeShaderTexPointDensity *shader_point_density = (NodeShaderTexPointDensity *)node->storage; + Object *ob = (Object *)node->id; + + PointerRNA ob_ptr, obdata_ptr; + RNA_id_pointer_create((ID *)ob, &ob_ptr); + RNA_id_pointer_create(ob ? (ID *)ob->data : nullptr, &obdata_ptr); + + uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "object", DEFAULT_FLAGS, nullptr, ICON_NONE); + + if (node->id && shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { + PointerRNA dataptr; + RNA_id_pointer_create((ID *)node->id, &dataptr); + uiItemPointerR( + layout, ptr, "particle_system", &dataptr, "particle_systems", nullptr, ICON_NONE); + } + + uiItemR(layout, ptr, "space", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "radius", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "resolution", DEFAULT_FLAGS, nullptr, ICON_NONE); + if (shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { + uiItemR(layout, ptr, "particle_color_source", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + else { + uiItemR(layout, ptr, "vertex_color_source", DEFAULT_FLAGS, nullptr, ICON_NONE); + if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTWEIGHT) { + if (ob_ptr.data) { + uiItemPointerR( + layout, ptr, "vertex_attribute_name", &ob_ptr, "vertex_groups", "", ICON_NONE); + } + } + if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTCOL) { + if (obdata_ptr.data) { + uiItemPointerR( + layout, ptr, "vertex_attribute_name", &obdata_ptr, "vertex_colors", "", ICON_NONE); + } + } + } +} + +static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "object", DEFAULT_FLAGS, nullptr, 0); + uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, nullptr, 0); +} + +static void node_shader_buts_bump(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, 0); +} + +static void node_shader_buts_uvmap(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, nullptr, 0); + + if (!RNA_boolean_get(ptr, "from_instancer")) { + PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); + + if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { + PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); + uiItemPointerR(layout, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); + } + } +} + +static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); + if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { + PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); + + if (U.experimental.use_sculpt_vertex_colors && + RNA_collection_length(&dataptr, "sculpt_vertex_colors")) { + uiItemPointerR( + layout, ptr, "layer_name", &dataptr, "sculpt_vertex_colors", "", ICON_GROUP_VCOL); + } + else { + uiItemPointerR(layout, ptr, "layer_name", &dataptr, "vertex_colors", "", ICON_GROUP_VCOL); + } + } + else { + uiItemL(layout, "No mesh in active object.", ICON_ERROR); + } +} + +static void node_shader_buts_uvalongstroke(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_tips", DEFAULT_FLAGS, nullptr, 0); +} + +static void node_shader_buts_normal_map(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", 0); + + if (RNA_enum_get(ptr, "space") == SHD_SPACE_TANGENT) { + PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); + + if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { + PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); + uiItemPointerR(layout, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); + } + else { + uiItemR(layout, ptr, "uv_map", DEFAULT_FLAGS, "", 0); + } + } +} + +static void node_shader_buts_displacement(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", 0); +} + +static void node_shader_buts_tangent(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiLayout *split, *row; + + split = uiLayoutSplit(layout, 0.0f, false); + + uiItemR(split, ptr, "direction_type", DEFAULT_FLAGS, "", 0); + + row = uiLayoutRow(split, false); + + if (RNA_enum_get(ptr, "direction_type") == SHD_TANGENT_UVMAP) { + PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); + + if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { + PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); + uiItemPointerR(row, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); + } + else { + uiItemR(row, ptr, "uv_map", DEFAULT_FLAGS, "", 0); + } + } + else { + uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, 0); + } +} + +static void node_shader_buts_glossy(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_principled(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "subsurface_method", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_anisotropic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_subsurface(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_toon(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "component", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_hair(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "component", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_principled_hair(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "parametrization", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row; + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, true); + + if (RNA_enum_get(ptr, "mode") == NODE_IES_INTERNAL) { + uiItemR(row, ptr, "ies", DEFAULT_FLAGS, "", ICON_NONE); + } + else { + uiItemR(row, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); + } +} + +static void node_shader_buts_script(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row; + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, true); + + if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_INTERNAL) { + uiItemR(row, ptr, "script", DEFAULT_FLAGS, "", ICON_NONE); + } + else { + uiItemR(row, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); + } + + uiItemO(row, "", ICON_FILE_REFRESH, "node.shader_script_update"); +} + +static void node_shader_buts_script_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiItemS(layout); + + node_shader_buts_script(layout, C, ptr); + +#if 0 /* not implemented yet */ + if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_EXTERNAL) { + uiItemR(layout, ptr, "use_auto_update", DEFAULT_FLAGS, nullptr, ICON_NONE); + } +#endif +} + +static void node_buts_output_shader(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "target", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_buts_output_linestyle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row, *col; + + col = uiLayoutColumn(layout, false); + row = uiLayoutRow(col, true); + uiItemR(row, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_shader_buts_bevel(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_shader_buts_ambient_occlusion(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "inside", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "only_local", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "noise_dimensions", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_shader_buts_output_aov(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "name", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +/* only once called */ +static void node_shader_set_butfunc(bNodeType *ntype) +{ + switch (ntype->type) { + case SH_NODE_NORMAL: + ntype->draw_buttons = node_buts_normal; + break; + case SH_NODE_CURVE_VEC: + ntype->draw_buttons = node_buts_curvevec; + break; + case SH_NODE_CURVE_RGB: + ntype->draw_buttons = node_buts_curvecol; + break; + case SH_NODE_MAPPING: + ntype->draw_buttons = node_shader_buts_mapping; + break; + case SH_NODE_VALUE: + ntype->draw_buttons = node_buts_value; + break; + case SH_NODE_RGB: + ntype->draw_buttons = node_buts_rgb; + break; + case SH_NODE_MIX_RGB: + ntype->draw_buttons = node_buts_mix_rgb; + break; + case SH_NODE_VALTORGB: + ntype->draw_buttons = node_buts_colorramp; + break; + case SH_NODE_CLAMP: + ntype->draw_buttons = node_shader_buts_clamp; + break; + case SH_NODE_MAP_RANGE: + ntype->draw_buttons = node_shader_buts_map_range; + break; + case SH_NODE_MATH: + ntype->draw_buttons = node_buts_math; + break; + case SH_NODE_VECTOR_MATH: + ntype->draw_buttons = node_shader_buts_vect_math; + break; + case SH_NODE_VECTOR_ROTATE: + ntype->draw_buttons = node_shader_buts_vector_rotate; + break; + case SH_NODE_VECT_TRANSFORM: + ntype->draw_buttons = node_shader_buts_vect_transform; + break; + case SH_NODE_ATTRIBUTE: + ntype->draw_buttons = node_shader_buts_attribute; + break; + case SH_NODE_WIREFRAME: + ntype->draw_buttons = node_shader_buts_wireframe; + break; + case SH_NODE_TEX_SKY: + ntype->draw_buttons = node_shader_buts_tex_sky; + break; + case SH_NODE_TEX_IMAGE: + ntype->draw_buttons = node_shader_buts_tex_image; + ntype->draw_buttons_ex = node_shader_buts_tex_image_ex; + break; + case SH_NODE_TEX_ENVIRONMENT: + ntype->draw_buttons = node_shader_buts_tex_environment; + ntype->draw_buttons_ex = node_shader_buts_tex_environment_ex; + break; + case SH_NODE_TEX_GRADIENT: + ntype->draw_buttons = node_shader_buts_tex_gradient; + break; + case SH_NODE_TEX_MAGIC: + ntype->draw_buttons = node_shader_buts_tex_magic; + break; + case SH_NODE_TEX_BRICK: + ntype->draw_buttons = node_shader_buts_tex_brick; + break; + case SH_NODE_TEX_WAVE: + ntype->draw_buttons = node_shader_buts_tex_wave; + break; + case SH_NODE_TEX_MUSGRAVE: + ntype->draw_buttons = node_shader_buts_tex_musgrave; + break; + case SH_NODE_TEX_VORONOI: + ntype->draw_buttons = node_shader_buts_tex_voronoi; + break; + case SH_NODE_TEX_NOISE: + ntype->draw_buttons = node_shader_buts_tex_noise; + break; + case SH_NODE_TEX_POINTDENSITY: + ntype->draw_buttons = node_shader_buts_tex_pointdensity; + break; + case SH_NODE_TEX_COORD: + ntype->draw_buttons = node_shader_buts_tex_coord; + break; + case SH_NODE_BUMP: + ntype->draw_buttons = node_shader_buts_bump; + break; + case SH_NODE_NORMAL_MAP: + ntype->draw_buttons = node_shader_buts_normal_map; + break; + case SH_NODE_DISPLACEMENT: + case SH_NODE_VECTOR_DISPLACEMENT: + ntype->draw_buttons = node_shader_buts_displacement; + break; + case SH_NODE_TANGENT: + ntype->draw_buttons = node_shader_buts_tangent; + break; + case SH_NODE_BSDF_GLOSSY: + case SH_NODE_BSDF_GLASS: + case SH_NODE_BSDF_REFRACTION: + ntype->draw_buttons = node_shader_buts_glossy; + break; + case SH_NODE_BSDF_PRINCIPLED: + ntype->draw_buttons = node_shader_buts_principled; + break; + case SH_NODE_BSDF_ANISOTROPIC: + ntype->draw_buttons = node_shader_buts_anisotropic; + break; + case SH_NODE_SUBSURFACE_SCATTERING: + ntype->draw_buttons = node_shader_buts_subsurface; + break; + case SH_NODE_BSDF_TOON: + ntype->draw_buttons = node_shader_buts_toon; + break; + case SH_NODE_BSDF_HAIR: + ntype->draw_buttons = node_shader_buts_hair; + break; + case SH_NODE_BSDF_HAIR_PRINCIPLED: + ntype->draw_buttons = node_shader_buts_principled_hair; + break; + case SH_NODE_SCRIPT: + ntype->draw_buttons = node_shader_buts_script; + ntype->draw_buttons_ex = node_shader_buts_script_ex; + break; + case SH_NODE_UVMAP: + ntype->draw_buttons = node_shader_buts_uvmap; + break; + case SH_NODE_VERTEX_COLOR: + ntype->draw_buttons = node_shader_buts_vertex_color; + break; + case SH_NODE_UVALONGSTROKE: + ntype->draw_buttons = node_shader_buts_uvalongstroke; + break; + case SH_NODE_OUTPUT_MATERIAL: + case SH_NODE_OUTPUT_LIGHT: + case SH_NODE_OUTPUT_WORLD: + ntype->draw_buttons = node_buts_output_shader; + break; + case SH_NODE_OUTPUT_LINESTYLE: + ntype->draw_buttons = node_buts_output_linestyle; + break; + case SH_NODE_TEX_IES: + ntype->draw_buttons = node_shader_buts_ies; + break; + case SH_NODE_BEVEL: + ntype->draw_buttons = node_shader_buts_bevel; + break; + case SH_NODE_AMBIENT_OCCLUSION: + ntype->draw_buttons = node_shader_buts_ambient_occlusion; + break; + case SH_NODE_TEX_WHITE_NOISE: + ntype->draw_buttons = node_shader_buts_white_noise; + break; + case SH_NODE_OUTPUT_AOV: + ntype->draw_buttons = node_shader_buts_output_aov; + break; + } +} + +/* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */ + +static void node_buts_image_views(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr, + PointerRNA *imaptr) +{ + uiLayout *col; + + if (!imaptr->data) { + return; + } + + col = uiLayoutColumn(layout, false); + + if (RNA_boolean_get(ptr, "has_views")) { + if (RNA_enum_get(ptr, "view") == 0) { + uiItemR(col, ptr, "view", DEFAULT_FLAGS, nullptr, ICON_CAMERA_STEREO); + } + else { + uiItemR(col, ptr, "view", DEFAULT_FLAGS, nullptr, ICON_SCENE); + } + } +} + +static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + PointerRNA iuserptr; + RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); + uiLayoutSetContextPointer(layout, "image_user", &iuserptr); + uiTemplateID(layout, + C, + ptr, + "image", + "IMAGE_OT_new", + "IMAGE_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + if (!node->id) { + return; + } + + PointerRNA imaptr = RNA_pointer_get(ptr, "image"); + + node_buts_image_user(layout, C, ptr, &imaptr, &iuserptr, true, true); + + node_buts_image_views(layout, C, ptr, &imaptr); +} + +static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + PointerRNA iuserptr; + RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); + uiLayoutSetContextPointer(layout, "image_user", &iuserptr); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, true); +} + +static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + uiLayout *col, *row; + + uiTemplateID(layout, + C, + ptr, + "scene", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (!node->id) { + return; + } + + col = uiLayoutColumn(layout, false); + row = uiLayoutRow(col, true); + uiItemR(row, ptr, "layer", DEFAULT_FLAGS, "", ICON_NONE); + + PropertyRNA *prop = RNA_struct_find_property(ptr, "layer"); + const char *layer_name; + if (!(RNA_property_enum_identifier( + C, ptr, prop, RNA_property_enum_get(ptr, prop), &layer_name))) { + return; + } + + PointerRNA scn_ptr; + char scene_name[MAX_ID_NAME - 2]; + scn_ptr = RNA_pointer_get(ptr, "scene"); + RNA_string_get(&scn_ptr, "name", scene_name); + + PointerRNA op_ptr; + uiItemFullO( + row, "RENDER_OT_render", "", ICON_RENDER_STILL, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_string_set(&op_ptr, "layer", layer_name); + RNA_string_set(&op_ptr, "scene", scene_name); +} + +static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col, *row; + + col = uiLayoutColumn(layout, false); + const int filter = RNA_enum_get(ptr, "filter_type"); + const int reference = RNA_boolean_get(ptr, "use_variable_size"); + + uiItemR(col, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); + if (filter != R_FILTER_FAST_GAUSS) { + uiItemR(col, ptr, "use_variable_size", DEFAULT_FLAGS, nullptr, ICON_NONE); + if (!reference) { + uiItemR(col, ptr, "use_bokeh", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + uiItemR(col, ptr, "use_gamma_correction", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + + uiItemR(col, ptr, "use_relative", DEFAULT_FLAGS, nullptr, ICON_NONE); + + if (RNA_boolean_get(ptr, "use_relative")) { + uiItemL(col, IFACE_("Aspect Correction"), ICON_NONE); + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "aspect_correction", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "factor_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); + uiItemR(col, ptr, "factor_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); + } + else { + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "size_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); + uiItemR(col, ptr, "size_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); + } + uiItemR(col, ptr, "use_extended_bounds", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_wrap", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemL(col, IFACE_("Center:"), ICON_NONE); + uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); + uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); + + uiItemS(layout); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiItemS(layout); + + uiItemR(layout, ptr, "spin", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "zoom", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_bilateralblur(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "sigma_color", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "sigma_space", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiLayout *sub, *col; + + col = uiLayoutColumn(layout, false); + uiItemL(col, IFACE_("Bokeh Type:"), ICON_NONE); + uiItemR(col, ptr, "bokeh", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "use_gamma_correction", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_zbuffer") == true); + uiItemR(col, ptr, "f_stop", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiTemplateID(layout, + C, + ptr, + "scene", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, nullptr, ICON_NONE); + sub = uiLayoutColumn(col, false); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_zbuffer") == false); + uiItemR(sub, ptr, "z_scale", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + + uiItemR(col, ptr, "threshold", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "contrast_limit", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE); +} + +/* qdn: glare node */ +static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "glare_type", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "quality", DEFAULT_FLAGS, "", ICON_NONE); + + if (RNA_enum_get(ptr, "glare_type") != 1) { + uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "glare_type") != 0) { + uiItemR( + layout, ptr, "color_modulation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + } + } + + uiItemR(layout, ptr, "mix", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "glare_type") == 2) { + uiItemR(layout, ptr, "streaks", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "angle_offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + if (RNA_enum_get(ptr, "glare_type") == 0 || RNA_enum_get(ptr, "glare_type") == 2) { + uiItemR(layout, ptr, "fade", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "glare_type") == 0) { + uiItemR(layout, ptr, "use_rotate_45", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + } + if (RNA_enum_get(ptr, "glare_type") == 1) { + uiItemR(layout, ptr, "size", DEFAULT_FLAGS, nullptr, ICON_NONE); + } +} + +static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "tonemap_type", DEFAULT_FLAGS, "", ICON_NONE); + if (RNA_enum_get(ptr, "tonemap_type") == 0) { + uiItemR(col, ptr, "key", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + else { + uiItemR(col, ptr, "intensity", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "adaptation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "correction", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + } +} + +static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "use_projector", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(col, false); + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_projector") == false); + uiItemR(col, ptr, "use_jitter", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_fit", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "factor", DEFAULT_FLAGS, IFACE_("Blur"), ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemL(col, IFACE_("Speed:"), ICON_NONE); + uiItemR(col, ptr, "speed_min", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(col, ptr, "speed_max", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + + uiItemR(layout, ptr, "use_curved", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "axis", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + uiItemR(layout, ptr, "use_crop_size", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "relative", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + if (RNA_boolean_get(ptr, "relative")) { + uiItemR(col, ptr, "rel_min_x", DEFAULT_FLAGS, IFACE_("Left"), ICON_NONE); + uiItemR(col, ptr, "rel_max_x", DEFAULT_FLAGS, IFACE_("Right"), ICON_NONE); + uiItemR(col, ptr, "rel_min_y", DEFAULT_FLAGS, IFACE_("Up"), ICON_NONE); + uiItemR(col, ptr, "rel_max_y", DEFAULT_FLAGS, IFACE_("Down"), ICON_NONE); + } + else { + uiItemR(col, ptr, "min_x", DEFAULT_FLAGS, IFACE_("Left"), ICON_NONE); + uiItemR(col, ptr, "max_x", DEFAULT_FLAGS, IFACE_("Right"), ICON_NONE); + uiItemR(col, ptr, "min_y", DEFAULT_FLAGS, IFACE_("Up"), ICON_NONE); + uiItemR(col, ptr, "max_y", DEFAULT_FLAGS, IFACE_("Down"), ICON_NONE); + } +} + +static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row, *col; + + col = uiLayoutColumn(layout, false); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(col, ptr, "factor", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_double_edge_mask(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + + uiItemL(col, IFACE_("Inner Edge:"), ICON_NONE); + uiItemR(col, ptr, "inner_mode", DEFAULT_FLAGS, "", ICON_NONE); + uiItemL(col, IFACE_("Buffer Edge:"), ICON_NONE); + uiItemR(col, ptr, "edge_mode", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *sub, *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "size", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_min", DEFAULT_FLAGS, nullptr, ICON_NONE); + sub = uiLayoutColumn(col, false); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_min")); + uiItemR(sub, ptr, "min", DEFAULT_FLAGS, "", ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_max", DEFAULT_FLAGS, nullptr, ICON_NONE); + sub = uiLayoutColumn(col, false); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_max")); + uiItemR(sub, ptr, "max", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_premultiply", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "premul", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_antialias_z", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE); + switch (RNA_enum_get(ptr, "mode")) { + case CMP_NODE_DILATEERODE_DISTANCE_THRESH: + uiItemR(layout, ptr, "edge", DEFAULT_FLAGS, nullptr, ICON_NONE); + break; + case CMP_NODE_DILATEERODE_DISTANCE_FEATHER: + uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, nullptr, ICON_NONE); + break; + } +} + +static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "threshold_neighbor", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void node_composit_buts_distance_matte(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *col, *row; + + col = uiLayoutColumn(layout, true); + + uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row, *col; + + uiItemL(layout, IFACE_("Despill Channel:"), ICON_NONE); + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "limit_method") == 0) { + uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + } + + uiItemR(col, ptr, "ratio", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_unspill", DEFAULT_FLAGS, nullptr, ICON_NONE); + if (RNA_boolean_get(ptr, "use_unspill") == true) { + uiItemR(col, ptr, "unspill_red", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "unspill_green", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "unspill_blue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + } +} + +static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + /*uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); Removed for now */ + uiItemR(col, ptr, "gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + /*uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); Removed for now*/ +} + +static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "color_hue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "color_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "color_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void node_composit_buts_channel_matte(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *col, *row; + + uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "color_space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemL(col, IFACE_("Key Channel:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "matte_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + + uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, nullptr, ICON_NONE); + if (RNA_enum_get(ptr, "limit_method") == 0) { + uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + } + + uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "index", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_antialiasing", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + PointerRNA imfptr = RNA_pointer_get(ptr, "format"); + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + + if (multilayer) { + uiItemL(layout, IFACE_("Path:"), ICON_NONE); + } + else { + uiItemL(layout, IFACE_("Base Path:"), ICON_NONE); + } + uiItemR(layout, ptr, "base_path", DEFAULT_FLAGS, "", ICON_NONE); +} +static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + Scene *scene = CTX_data_scene(C); + PointerRNA imfptr = RNA_pointer_get(ptr, "format"); + PointerRNA active_input_ptr, op_ptr; + uiLayout *row, *col; + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + const bool is_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR; + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; + + node_composit_buts_file_output(layout, C, ptr); + uiTemplateImageSettings(layout, &imfptr, false); + + /* disable stereo output for multilayer, too much work for something that no one will use */ + /* if someone asks for that we can implement it */ + if (is_multiview) { + uiTemplateImageFormatViews(layout, &imfptr, nullptr); + } + + uiItemS(layout); + + uiItemO(layout, IFACE_("Add Input"), ICON_ADD, "NODE_OT_output_file_add_socket"); + + row = uiLayoutRow(layout, false); + col = uiLayoutColumn(row, true); + + const int active_index = RNA_int_get(ptr, "active_input_index"); + /* using different collection properties if multilayer format is enabled */ + if (multilayer) { + uiTemplateList(col, + C, + "UI_UL_list", + "file_output_node", + ptr, + "layer_slots", + ptr, + "active_input_index", + nullptr, + 0, + 0, + 0, + 0, + false, + false); + RNA_property_collection_lookup_int( + ptr, RNA_struct_find_property(ptr, "layer_slots"), active_index, &active_input_ptr); + } + else { + uiTemplateList(col, + C, + "UI_UL_list", + "file_output_node", + ptr, + "file_slots", + ptr, + "active_input_index", + nullptr, + 0, + 0, + 0, + 0, + false, + false); + RNA_property_collection_lookup_int( + ptr, RNA_struct_find_property(ptr, "file_slots"), active_index, &active_input_ptr); + } + /* XXX collection lookup does not return the ID part of the pointer, + * setting this manually here */ + active_input_ptr.owner_id = ptr->owner_id; + + col = uiLayoutColumn(row, true); + wmOperatorType *ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false); + uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_enum_set(&op_ptr, "direction", 1); + uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_enum_set(&op_ptr, "direction", 2); + + if (active_input_ptr.data) { + if (multilayer) { + col = uiLayoutColumn(layout, true); + + uiItemL(col, IFACE_("Layer:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, &active_input_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE); + uiItemFullO(row, + "NODE_OT_output_file_remove_active_socket", + "", + ICON_X, + nullptr, + WM_OP_EXEC_DEFAULT, + UI_ITEM_R_ICON_ONLY, + nullptr); + } + else { + col = uiLayoutColumn(layout, true); + + uiItemL(col, IFACE_("File Subpath:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, &active_input_ptr, "path", DEFAULT_FLAGS, "", ICON_NONE); + uiItemFullO(row, + "NODE_OT_output_file_remove_active_socket", + "", + ICON_X, + nullptr, + WM_OP_EXEC_DEFAULT, + UI_ITEM_R_ICON_ONLY, + nullptr); + + /* format details for individual files */ + imfptr = RNA_pointer_get(&active_input_ptr, "format"); + + col = uiLayoutColumn(layout, true); + uiItemL(col, IFACE_("Format:"), ICON_NONE); + uiItemR(col, &active_input_ptr, "use_node_format", DEFAULT_FLAGS, nullptr, ICON_NONE); + + const bool is_socket_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR; + const bool use_node_format = RNA_boolean_get(&active_input_ptr, "use_node_format"); + + if ((!is_exr && use_node_format) || (!is_socket_exr && !use_node_format)) { + uiItemR(col, &active_input_ptr, "save_as_render", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, use_node_format == false); + uiTemplateImageSettings(col, &imfptr, false); + + if (is_multiview) { + uiTemplateImageFormatViews(layout, &imfptr, nullptr); + } + } + } +} + +static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", ICON_NONE); + + if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) { + uiLayout *row; + uiItemR(layout, ptr, "frame_method", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "offset_x", DEFAULT_FLAGS, "X", ICON_NONE); + uiItemR(row, ptr, "offset_y", DEFAULT_FLAGS, "Y", ICON_NONE); + } +} + +static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "invert_rgb", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "invert_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mapping", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *split, *col, *row; + + uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "correction_method") == 0) { + + split = uiLayoutSplit(layout, 0.0f, false); + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "lift", true, true, false, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "lift", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "gamma", true, true, true, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "gain", true, true, true, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "gain", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + else { + + split = uiLayoutSplit(layout, 0.0f, false); + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "offset", true, true, false, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "offset_basis", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "power", true, true, false, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "power", DEFAULT_FLAGS, nullptr, ICON_NONE); + + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "slope", true, true, false, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "slope", DEFAULT_FLAGS, nullptr, ICON_NONE); + } +} +static void node_composit_buts_colorbalance_ex(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "correction_method") == 0) { + + uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true); + uiItemR(layout, ptr, "lift", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiTemplateColorPicker(layout, ptr, "gamma", true, true, true, true); + uiItemR(layout, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiTemplateColorPicker(layout, ptr, "gain", true, true, true, true); + uiItemR(layout, ptr, "gain", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + else { + uiTemplateColorPicker(layout, ptr, "offset", true, true, false, true); + uiItemR(layout, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiTemplateColorPicker(layout, ptr, "power", true, true, false, true); + uiItemR(layout, ptr, "power", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiTemplateColorPicker(layout, ptr, "slope", true, true, false, true); + uiItemR(layout, ptr, "slope", DEFAULT_FLAGS, nullptr, ICON_NONE); + } +} + +static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + CurveMapping *cumap = (CurveMapping *)node->storage; + + if (_sample_col[0] != SAMPLE_FLT_ISNONE) { + cumap->flag |= CUMA_DRAW_SAMPLE; + copy_v3_v3(cumap->sample, _sample_col); + } + else { + cumap->flag &= ~CUMA_DRAW_SAMPLE; + } + + uiTemplateCurveMapping(layout, ptr, "mapping", 'h', false, false, false, false); +} + +static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); +} + +static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + PointerRNA clipptr; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (!node->id) { + return; + } + + clipptr = RNA_pointer_get(ptr, "clip"); + + uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings"); +} + +static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (!node->id) { + return; + } + + uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_relative", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "wrap_axis", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (!node->id) { + return; + } + + uiItemR(layout, ptr, "distortion_type", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_colorcorrection(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *row; + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "red", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "green", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "blue", DEFAULT_FLAGS, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, "", ICON_NONE); + uiItemL(row, IFACE_("Saturation"), ICON_NONE); + uiItemL(row, IFACE_("Contrast"), ICON_NONE); + uiItemL(row, IFACE_("Gamma"), ICON_NONE); + uiItemL(row, IFACE_("Gain"), ICON_NONE); + uiItemL(row, IFACE_("Lift"), ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Master"), ICON_NONE); + uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Highlights"), ICON_NONE); + uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Midtones"), ICON_NONE); + uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Shadows"), ICON_NONE); + uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void node_composit_buts_colorcorrection_ex(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *row; + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "red", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "green", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "blue", DEFAULT_FLAGS, nullptr, ICON_NONE); + row = layout; + uiItemL(row, IFACE_("Saturation"), ICON_NONE); + uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + uiItemL(row, IFACE_("Contrast"), ICON_NONE); + uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + uiItemL(row, IFACE_("Gamma"), ICON_NONE); + uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + uiItemL(row, IFACE_("Gain"), ICON_NONE); + uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + uiItemL(row, IFACE_("Lift"), ICON_NONE); + uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "check", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_switch_view_ex(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *UNUSED(ptr)) +{ + uiItemFullO(layout, + "NODE_OT_switch_view_update", + "Update Views", + ICON_FILE_REFRESH, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + nullptr); +} + +static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row; + + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "x", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "y", DEFAULT_FLAGS, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "flaps", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "rounding", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(layout, ptr, "catadioptric", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(layout, ptr, "shift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_variable_size", DEFAULT_FLAGS, nullptr, ICON_NONE); + // uiItemR(layout, ptr, "f_stop", DEFAULT_FLAGS, nullptr, ICON_NONE); /* UNUSED */ + uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_extended_bounds", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_backdrop_viewer( + SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) +{ + // node_composit_backdrop_canvas(snode, backdrop, node, x, y); + if (node->custom1 == 0) { + const float backdropWidth = backdrop->x; + const float backdropHeight = backdrop->y; + const float cx = x + snode->zoom * backdropWidth * node->custom3; + const float cy = y + snode->zoom * backdropHeight * node->custom4; + const float cross_size = 12 * U.pixelsize; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3f(1.0f, 1.0f, 1.0f); + + immBegin(GPU_PRIM_LINES, 4); + immVertex2f(pos, cx - cross_size, cy - cross_size); + immVertex2f(pos, cx + cross_size, cy + cross_size); + immVertex2f(pos, cx + cross_size, cy - cross_size); + immVertex2f(pos, cx - cross_size, cy + cross_size); + immEnd(); + + immUnbindProgram(); + } +} + +static void node_composit_backdrop_boxmask( + SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) +{ + NodeBoxMask *boxmask = (NodeBoxMask *)node->storage; + const float backdropWidth = backdrop->x; + const float backdropHeight = backdrop->y; + const float aspect = backdropWidth / backdropHeight; + const float rad = -boxmask->rotation; + const float cosine = cosf(rad); + const float sine = sinf(rad); + const float halveBoxWidth = backdropWidth * (boxmask->width / 2.0f); + const float halveBoxHeight = backdropHeight * (boxmask->height / 2.0f) * aspect; + + float cx, cy, x1, x2, x3, x4; + float y1, y2, y3, y4; + + cx = x + snode->zoom * backdropWidth * boxmask->x; + cy = y + snode->zoom * backdropHeight * boxmask->y; + + x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->zoom; + x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->zoom; + x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; + x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; + y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; + y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; + y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; + y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3f(1.0f, 1.0f, 1.0f); + + immBegin(GPU_PRIM_LINE_LOOP, 4); + immVertex2f(pos, x1, y1); + immVertex2f(pos, x2, y2); + immVertex2f(pos, x3, y3); + immVertex2f(pos, x4, y4); + immEnd(); + + immUnbindProgram(); +} + +static void node_composit_backdrop_ellipsemask( + SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) +{ + NodeEllipseMask *ellipsemask = (NodeEllipseMask *)node->storage; + const float backdropWidth = backdrop->x; + const float backdropHeight = backdrop->y; + const float aspect = backdropWidth / backdropHeight; + const float rad = -ellipsemask->rotation; + const float cosine = cosf(rad); + const float sine = sinf(rad); + const float halveBoxWidth = backdropWidth * (ellipsemask->width / 2.0f); + const float halveBoxHeight = backdropHeight * (ellipsemask->height / 2.0f) * aspect; + + float cx, cy, x1, x2, x3, x4; + float y1, y2, y3, y4; + + cx = x + snode->zoom * backdropWidth * ellipsemask->x; + cy = y + snode->zoom * backdropHeight * ellipsemask->y; + + x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->zoom; + x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->zoom; + x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; + x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->zoom; + y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; + y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->zoom; + y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; + y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3f(1.0f, 1.0f, 1.0f); + + immBegin(GPU_PRIM_LINE_LOOP, 4); + immVertex2f(pos, x1, y1); + immVertex2f(pos, x2, y2); + immVertex2f(pos, x3, y3); + immVertex2f(pos, x4, y4); + immEnd(); + + immUnbindProgram(); +} + +static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row; + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "x", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(row, ptr, "y", DEFAULT_FLAGS, nullptr, ICON_NONE); + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_viewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "tile_order", DEFAULT_FLAGS, nullptr, ICON_NONE); + if (RNA_enum_get(ptr, "tile_order") == 0) { + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, nullptr, ICON_NONE); + } +} + +static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + uiTemplateID(layout, + C, + ptr, + "mask", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "size_source", DEFAULT_FLAGS, "", ICON_NONE); + + if (node->custom1 & (CMP_NODEFLAG_MASK_FIXED | CMP_NODEFLAG_MASK_FIXED_SCENE)) { + uiItemR(layout, ptr, "size_x", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "size_y", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + + uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, nullptr, ICON_NONE); + if (node->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) { + uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, nullptr, ICON_NONE); + } +} + +static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (node->id) { + MovieClip *clip = (MovieClip *)node->id; + uiLayout *col; + PointerRNA tracking_ptr; + + RNA_pointer_create(&clip->id, &RNA_MovieTracking, &clip->tracking, &tracking_ptr); + + col = uiLayoutColumn(layout, true); + uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA); + } +} + +static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + /* bNode *node = (bNode*)ptr->data; */ /* UNUSED */ + + uiItemR(layout, ptr, "blur_pre", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "screen_balance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "despill_factor", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "despill_balance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "edge_kernel_radius", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "edge_kernel_tolerance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "clip_black", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "clip_white", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "dilate_distance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "feather_falloff", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "feather_distance", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "blur_post", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (node->id) { + MovieClip *clip = (MovieClip *)node->id; + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *object; + uiLayout *col; + PointerRNA tracking_ptr; + NodeTrackPosData *data = (NodeTrackPosData *)node->storage; + + RNA_pointer_create(&clip->id, &RNA_MovieTracking, tracking, &tracking_ptr); + + col = uiLayoutColumn(layout, false); + uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA); + + object = BKE_tracking_object_get_named(tracking, data->tracking_object); + if (object) { + PointerRNA object_ptr; + + RNA_pointer_create(&clip->id, &RNA_MovieTrackingObject, object, &object_ptr); + + uiItemPointerR(col, ptr, "track_name", &object_ptr, "tracks", "", ICON_ANIM_DATA); + } + else { + uiItemR(layout, ptr, "track_name", DEFAULT_FLAGS, "", ICON_ANIM_DATA); + } + + uiItemR(layout, ptr, "position", DEFAULT_FLAGS, nullptr, ICON_NONE); + + if (ELEM(node->custom1, CMP_TRACKPOS_RELATIVE_FRAME, CMP_TRACKPOS_ABSOLUTE_FRAME)) { + uiItemR(layout, ptr, "frame_relative", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + } +} + +static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)node->storage; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (node->id) { + MovieClip *clip = (MovieClip *)node->id; + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *object; + uiLayout *col; + PointerRNA tracking_ptr; + + RNA_pointer_create(&clip->id, &RNA_MovieTracking, tracking, &tracking_ptr); + + col = uiLayoutColumn(layout, false); + uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA); + + object = BKE_tracking_object_get_named(tracking, data->tracking_object); + if (object) { + PointerRNA object_ptr; + + RNA_pointer_create(&clip->id, &RNA_MovieTrackingObject, object, &object_ptr); + + uiItemPointerR( + col, ptr, "plane_track_name", &object_ptr, "plane_tracks", "", ICON_ANIM_DATA); + } + else { + uiItemR(layout, ptr, "plane_track_name", 0, "", ICON_ANIM_DATA); + } + } + + uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, nullptr, ICON_NONE); + if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { + uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, nullptr, ICON_NONE); + } +} + +static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout), + bContext *UNUSED(C), + PointerRNA *UNUSED(ptr)) +{ +} + +static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, "", ICON_NONE); + uiItemR(layout, ptr, "ray_length", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void node_composit_buts_cryptomatte_legacy(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *col = uiLayoutColumn(layout, true); + + uiItemL(col, IFACE_("Matte Objects:"), ICON_NONE); + + uiLayout *row = uiLayoutRow(col, true); + uiTemplateCryptoPicker(row, ptr, "add", ICON_ADD); + uiTemplateCryptoPicker(row, ptr, "remove", ICON_REMOVE); + + uiItemR(col, ptr, "matte_id", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_composit_buts_cryptomatte_legacy_ex(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *UNUSED(ptr)) +{ + uiItemO(layout, IFACE_("Add Crypto Layer"), ICON_ADD, "NODE_OT_cryptomatte_layer_add"); + uiItemO(layout, IFACE_("Remove Crypto Layer"), ICON_REMOVE, "NODE_OT_cryptomatte_layer_remove"); +} + +static void node_composit_buts_cryptomatte(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + uiLayout *row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + uiLayout *col = uiLayoutColumn(layout, false); + if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) { + uiTemplateID(col, + C, + ptr, + "scene", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + } + else { + uiTemplateID(col, + C, + ptr, + "image", + nullptr, + "IMAGE_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + NodeCryptomatte *crypto = (NodeCryptomatte *)node->storage; + PointerRNA imaptr = RNA_pointer_get(ptr, "image"); + PointerRNA iuserptr; + RNA_pointer_create((ID *)ptr->owner_id, &RNA_ImageUser, &crypto->iuser, &iuserptr); + uiLayoutSetContextPointer(layout, "image_user", &iuserptr); + + node_buts_image_user(col, C, ptr, &imaptr, &iuserptr, false, false); + node_buts_image_views(col, C, ptr, &imaptr); + } + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "layer_name", 0, "", ICON_NONE); + uiItemL(col, IFACE_("Matte ID:"), ICON_NONE); + + row = uiLayoutRow(col, true); + uiItemR(row, ptr, "matte_id", DEFAULT_FLAGS, "", ICON_NONE); + uiTemplateCryptoPicker(row, ptr, "add", ICON_ADD); + uiTemplateCryptoPicker(row, ptr, "remove", ICON_REMOVE); +} + +static void node_composit_buts_brightcontrast(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_premultiply", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ +#ifndef WITH_OPENIMAGEDENOISE + uiItemL(layout, IFACE_("Disabled, built without OpenImageDenoise"), ICON_ERROR); +#else + /* Always supported through Accelerate framework BNNS on macOS. */ +# ifndef __APPLE__ + if (!BLI_cpu_support_sse41()) { + uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR); + } +# endif +#endif + + uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, nullptr, ICON_NONE); +} + +/* only once called */ +static void node_composit_set_butfunc(bNodeType *ntype) +{ + switch (ntype->type) { + case CMP_NODE_IMAGE: + ntype->draw_buttons = node_composit_buts_image; + ntype->draw_buttons_ex = node_composit_buts_image_ex; + break; + case CMP_NODE_R_LAYERS: + ntype->draw_buttons = node_composit_buts_viewlayers; + break; + case CMP_NODE_NORMAL: + ntype->draw_buttons = node_buts_normal; + break; + case CMP_NODE_CURVE_VEC: + ntype->draw_buttons = node_buts_curvevec; + break; + case CMP_NODE_CURVE_RGB: + ntype->draw_buttons = node_buts_curvecol; + break; + case CMP_NODE_VALUE: + ntype->draw_buttons = node_buts_value; + break; + case CMP_NODE_RGB: + ntype->draw_buttons = node_buts_rgb; + break; + case CMP_NODE_FLIP: + ntype->draw_buttons = node_composit_buts_flip; + break; + case CMP_NODE_SPLITVIEWER: + ntype->draw_buttons = node_composit_buts_splitviewer; + break; + case CMP_NODE_MIX_RGB: + ntype->draw_buttons = node_buts_mix_rgb; + break; + case CMP_NODE_VALTORGB: + ntype->draw_buttons = node_buts_colorramp; + break; + case CMP_NODE_CROP: + ntype->draw_buttons = node_composit_buts_crop; + break; + case CMP_NODE_BLUR: + ntype->draw_buttons = node_composit_buts_blur; + break; + case CMP_NODE_DBLUR: + ntype->draw_buttons = node_composit_buts_dblur; + break; + case CMP_NODE_BILATERALBLUR: + ntype->draw_buttons = node_composit_buts_bilateralblur; + break; + case CMP_NODE_DEFOCUS: + ntype->draw_buttons = node_composit_buts_defocus; + break; + case CMP_NODE_ANTIALIASING: + ntype->draw_buttons = node_composit_buts_antialiasing; + break; + case CMP_NODE_GLARE: + ntype->draw_buttons = node_composit_buts_glare; + break; + case CMP_NODE_TONEMAP: + ntype->draw_buttons = node_composit_buts_tonemap; + break; + case CMP_NODE_LENSDIST: + ntype->draw_buttons = node_composit_buts_lensdist; + break; + case CMP_NODE_VECBLUR: + ntype->draw_buttons = node_composit_buts_vecblur; + break; + case CMP_NODE_FILTER: + ntype->draw_buttons = node_composit_buts_filter; + break; + case CMP_NODE_MAP_VALUE: + ntype->draw_buttons = node_composit_buts_map_value; + break; + case CMP_NODE_MAP_RANGE: + ntype->draw_buttons = node_composit_buts_map_range; + break; + case CMP_NODE_TIME: + ntype->draw_buttons = node_buts_time; + break; + case CMP_NODE_ALPHAOVER: + ntype->draw_buttons = node_composit_buts_alphaover; + break; + case CMP_NODE_TEXTURE: + ntype->draw_buttons = node_buts_texture; + break; + case CMP_NODE_DILATEERODE: + ntype->draw_buttons = node_composit_buts_dilateerode; + break; + case CMP_NODE_INPAINT: + ntype->draw_buttons = node_composit_buts_inpaint; + break; + case CMP_NODE_DESPECKLE: + ntype->draw_buttons = node_composit_buts_despeckle; + break; + case CMP_NODE_OUTPUT_FILE: + ntype->draw_buttons = node_composit_buts_file_output; + ntype->draw_buttons_ex = node_composit_buts_file_output_ex; + break; + case CMP_NODE_DIFF_MATTE: + ntype->draw_buttons = node_composit_buts_diff_matte; + break; + case CMP_NODE_DIST_MATTE: + ntype->draw_buttons = node_composit_buts_distance_matte; + break; + case CMP_NODE_COLOR_SPILL: + ntype->draw_buttons = node_composit_buts_color_spill; + break; + case CMP_NODE_CHROMA_MATTE: + ntype->draw_buttons = node_composit_buts_chroma_matte; + break; + case CMP_NODE_COLOR_MATTE: + ntype->draw_buttons = node_composit_buts_color_matte; + break; + case CMP_NODE_SCALE: + ntype->draw_buttons = node_composit_buts_scale; + break; + case CMP_NODE_ROTATE: + ntype->draw_buttons = node_composit_buts_rotate; + break; + case CMP_NODE_CHANNEL_MATTE: + ntype->draw_buttons = node_composit_buts_channel_matte; + break; + case CMP_NODE_LUMA_MATTE: + ntype->draw_buttons = node_composit_buts_luma_matte; + break; + case CMP_NODE_MAP_UV: + ntype->draw_buttons = node_composit_buts_map_uv; + break; + case CMP_NODE_ID_MASK: + ntype->draw_buttons = node_composit_buts_id_mask; + break; + case CMP_NODE_DOUBLEEDGEMASK: + ntype->draw_buttons = node_composit_buts_double_edge_mask; + break; + case CMP_NODE_MATH: + ntype->draw_buttons = node_buts_math; + break; + case CMP_NODE_INVERT: + ntype->draw_buttons = node_composit_buts_invert; + break; + case CMP_NODE_PREMULKEY: + ntype->draw_buttons = node_composit_buts_premulkey; + break; + case CMP_NODE_VIEW_LEVELS: + ntype->draw_buttons = node_composit_buts_view_levels; + break; + case CMP_NODE_COLORBALANCE: + ntype->draw_buttons = node_composit_buts_colorbalance; + ntype->draw_buttons_ex = node_composit_buts_colorbalance_ex; + break; + case CMP_NODE_HUECORRECT: + ntype->draw_buttons = node_composit_buts_huecorrect; + break; + case CMP_NODE_ZCOMBINE: + ntype->draw_buttons = node_composit_buts_zcombine; + break; + case CMP_NODE_COMBYCCA: + case CMP_NODE_SEPYCCA: + ntype->draw_buttons = node_composit_buts_ycc; + break; + case CMP_NODE_MOVIECLIP: + ntype->draw_buttons = node_composit_buts_movieclip; + ntype->draw_buttons_ex = node_composit_buts_movieclip_ex; + break; + case CMP_NODE_STABILIZE2D: + ntype->draw_buttons = node_composit_buts_stabilize2d; + break; + case CMP_NODE_TRANSFORM: + ntype->draw_buttons = node_composit_buts_transform; + break; + case CMP_NODE_TRANSLATE: + ntype->draw_buttons = node_composit_buts_translate; + break; + case CMP_NODE_MOVIEDISTORTION: + ntype->draw_buttons = node_composit_buts_moviedistortion; + break; + case CMP_NODE_COLORCORRECTION: + ntype->draw_buttons = node_composit_buts_colorcorrection; + ntype->draw_buttons_ex = node_composit_buts_colorcorrection_ex; + break; + case CMP_NODE_SETALPHA: + ntype->draw_buttons = node_composit_buts_set_alpha; + break; + case CMP_NODE_SWITCH: + ntype->draw_buttons = node_composit_buts_switch; + break; + case CMP_NODE_SWITCH_VIEW: + ntype->draw_buttons_ex = node_composit_buts_switch_view_ex; + break; + case CMP_NODE_MASK_BOX: + ntype->draw_buttons = node_composit_buts_boxmask; + ntype->draw_backdrop = node_composit_backdrop_boxmask; + break; + case CMP_NODE_MASK_ELLIPSE: + ntype->draw_buttons = node_composit_buts_ellipsemask; + ntype->draw_backdrop = node_composit_backdrop_ellipsemask; + break; + case CMP_NODE_BOKEHIMAGE: + ntype->draw_buttons = node_composit_buts_bokehimage; + break; + case CMP_NODE_BOKEHBLUR: + ntype->draw_buttons = node_composit_buts_bokehblur; + break; + case CMP_NODE_VIEWER: + ntype->draw_buttons = node_composit_buts_viewer; + ntype->draw_buttons_ex = node_composit_buts_viewer_ex; + ntype->draw_backdrop = node_composit_backdrop_viewer; + break; + case CMP_NODE_COMPOSITE: + ntype->draw_buttons = node_composit_buts_composite; + break; + case CMP_NODE_MASK: + ntype->draw_buttons = node_composit_buts_mask; + break; + case CMP_NODE_KEYINGSCREEN: + ntype->draw_buttons = node_composit_buts_keyingscreen; + break; + case CMP_NODE_KEYING: + ntype->draw_buttons = node_composit_buts_keying; + break; + case CMP_NODE_TRACKPOS: + ntype->draw_buttons = node_composit_buts_trackpos; + break; + case CMP_NODE_PLANETRACKDEFORM: + ntype->draw_buttons = node_composit_buts_planetrackdeform; + break; + case CMP_NODE_CORNERPIN: + ntype->draw_buttons = node_composit_buts_cornerpin; + break; + case CMP_NODE_SUNBEAMS: + ntype->draw_buttons = node_composit_buts_sunbeams; + break; + case CMP_NODE_CRYPTOMATTE: + ntype->draw_buttons = node_composit_buts_cryptomatte; + break; + case CMP_NODE_CRYPTOMATTE_LEGACY: + ntype->draw_buttons = node_composit_buts_cryptomatte_legacy; + ntype->draw_buttons_ex = node_composit_buts_cryptomatte_legacy_ex; + break; + case CMP_NODE_BRIGHTCONTRAST: + ntype->draw_buttons = node_composit_buts_brightcontrast; + break; + case CMP_NODE_DENOISE: + ntype->draw_buttons = node_composit_buts_denoise; + break; + } +} + +/* ****************** BUTTON CALLBACKS FOR TEXTURE NODES ***************** */ + +static void node_texture_buts_bricks(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, IFACE_("Offset"), ICON_NONE); + uiItemR(col, ptr, "offset_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "squash", DEFAULT_FLAGS, IFACE_("Squash"), ICON_NONE); + uiItemR(col, ptr, "squash_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); +} + +static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + PointerRNA tex_ptr; + bNode *node = (bNode *)ptr->data; + ID *id = ptr->owner_id; + Tex *tex = (Tex *)node->storage; + uiLayout *col, *row; + + RNA_pointer_create(id, &RNA_Texture, tex, &tex_ptr); + + col = uiLayoutColumn(layout, false); + + switch (tex->type) { + case TEX_BLEND: + uiItemR(col, &tex_ptr, "progression", DEFAULT_FLAGS, "", ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR( + row, &tex_ptr, "use_flip_axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + break; + + case TEX_MARBLE: + row = uiLayoutRow(col, false); + uiItemR(row, &tex_ptr, "marble_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR( + row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + break; + + case TEX_MAGIC: + uiItemR(col, &tex_ptr, "noise_depth", DEFAULT_FLAGS, nullptr, ICON_NONE); + break; + + case TEX_STUCCI: + row = uiLayoutRow(col, false); + uiItemR(row, &tex_ptr, "stucci_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); + break; + + case TEX_WOOD: + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, &tex_ptr, "wood_type", DEFAULT_FLAGS, "", ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR( + row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + row = uiLayoutRow(col, false); + uiLayoutSetActive(row, !(ELEM(tex->stype, TEX_BAND, TEX_RING))); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + break; + + case TEX_CLOUDS: + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, &tex_ptr, "cloud_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(col, + &tex_ptr, + "noise_depth", + DEFAULT_FLAGS | UI_ITEM_R_EXPAND, + IFACE_("Depth"), + ICON_NONE); + break; + + case TEX_DISTNOISE: + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, &tex_ptr, "noise_distortion", DEFAULT_FLAGS, "", ICON_NONE); + break; + + case TEX_MUSGRAVE: + uiItemR(col, &tex_ptr, "musgrave_type", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); + break; + case TEX_VORONOI: + uiItemR(col, &tex_ptr, "distance_metric", DEFAULT_FLAGS, "", ICON_NONE); + if (tex->vn_distm == TEX_MINKOVSKY) { + uiItemR(col, &tex_ptr, "minkovsky_exponent", DEFAULT_FLAGS, nullptr, ICON_NONE); + } + uiItemR(col, &tex_ptr, "color_mode", DEFAULT_FLAGS, "", ICON_NONE); + break; + } +} + +static void node_texture_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiTemplateID(layout, + C, + ptr, + "image", + "IMAGE_OT_new", + "IMAGE_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); +} + +static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + PointerRNA iuserptr; + + RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false); +} + +static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); +} + +/* only once called */ +static void node_texture_set_butfunc(bNodeType *ntype) +{ + if (ntype->type >= TEX_NODE_PROC && ntype->type < TEX_NODE_PROC_MAX) { + ntype->draw_buttons = node_texture_buts_proc; + } + else { + switch (ntype->type) { + + case TEX_NODE_MATH: + ntype->draw_buttons = node_buts_math; + break; + + case TEX_NODE_MIX_RGB: + ntype->draw_buttons = node_buts_mix_rgb; + break; + + case TEX_NODE_VALTORGB: + ntype->draw_buttons = node_buts_colorramp; + break; + + case TEX_NODE_CURVE_RGB: + ntype->draw_buttons = node_buts_curvecol; + break; + + case TEX_NODE_CURVE_TIME: + ntype->draw_buttons = node_buts_time; + break; + + case TEX_NODE_TEXTURE: + ntype->draw_buttons = node_buts_texture; + break; + + case TEX_NODE_BRICKS: + ntype->draw_buttons = node_texture_buts_bricks; + break; + + case TEX_NODE_IMAGE: + ntype->draw_buttons = node_texture_buts_image; + ntype->draw_buttons_ex = node_texture_buts_image_ex; + break; + + case TEX_NODE_OUTPUT: + ntype->draw_buttons = node_texture_buts_output; + break; + } + } +} + +/* ****** init draw callbacks for all tree types, only called in usiblender.c, once ************ */ + +static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + bNode *node = (bNode *)ptr->data; + ED_node_tag_update_nodetree(bmain, ntree, node); +} + +static void node_socket_template_properties_update(bNodeType *ntype, bNodeSocketTemplate *stemp) +{ + StructRNA *srna = ntype->rna_ext.srna; + PropertyRNA *prop = RNA_struct_type_find_property(srna, stemp->identifier); + + if (prop) { + RNA_def_property_update_runtime(prop, (const void *)node_property_update_default); + } +} + +static void node_template_properties_update(bNodeType *ntype) +{ + bNodeSocketTemplate *stemp; + + if (ntype->inputs) { + for (stemp = ntype->inputs; stemp->type >= 0; stemp++) { + node_socket_template_properties_update(ntype, stemp); + } + } + if (ntype->outputs) { + for (stemp = ntype->outputs; stemp->type >= 0; stemp++) { + node_socket_template_properties_update(ntype, stemp); + } + } +} + +static void node_socket_undefined_draw(bContext *UNUSED(C), + uiLayout *layout, + PointerRNA *UNUSED(ptr), + PointerRNA *UNUSED(node_ptr), + const char *UNUSED(text)) +{ + uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR); +} + +static void node_socket_undefined_draw_color(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PointerRNA *UNUSED(node_ptr), + float *r_color) +{ + r_color[0] = 1.0f; + r_color[1] = 0.0f; + r_color[2] = 0.0f; + r_color[3] = 1.0f; +} + +static void node_socket_undefined_interface_draw(bContext *UNUSED(C), + uiLayout *layout, + PointerRNA *UNUSED(ptr)) +{ + uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR); +} + +static void node_socket_undefined_interface_draw_color(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + float *r_color) +{ + r_color[0] = 1.0f; + r_color[1] = 0.0f; + r_color[2] = 0.0f; + r_color[3] = 1.0f; +} + +void ED_node_init_butfuncs(void) +{ + /* Fallback types for undefined tree, nodes, sockets + * Defined in blenkernel, but not registered in type hashes. + */ + + /* default ui functions */ + NodeTypeUndefined.draw_nodetype = node_draw_default; + NodeTypeUndefined.draw_nodetype_prepare = node_update_default; + NodeTypeUndefined.select_area_func = node_select_area_default; + NodeTypeUndefined.tweak_area_func = node_tweak_area_default; + NodeTypeUndefined.draw_buttons = nullptr; + NodeTypeUndefined.draw_buttons_ex = nullptr; + NodeTypeUndefined.resize_area_func = node_resize_area_default; + + NodeSocketTypeUndefined.draw = node_socket_undefined_draw; + NodeSocketTypeUndefined.draw_color = node_socket_undefined_draw_color; + NodeSocketTypeUndefined.interface_draw = node_socket_undefined_interface_draw; + NodeSocketTypeUndefined.interface_draw_color = node_socket_undefined_interface_draw_color; + + /* node type ui functions */ + NODE_TYPES_BEGIN (ntype) { + /* default ui functions */ + ntype->draw_nodetype = node_draw_default; + ntype->draw_nodetype_prepare = node_update_default; + ntype->select_area_func = node_select_area_default; + ntype->tweak_area_func = node_tweak_area_default; + ntype->resize_area_func = node_resize_area_default; + + node_common_set_butfunc(ntype); + + node_composit_set_butfunc(ntype); + node_shader_set_butfunc(ntype); + node_texture_set_butfunc(ntype); + + /* define update callbacks for socket properties */ + node_template_properties_update(ntype); + } + NODE_TYPES_END; + + /* tree type icons */ + ntreeType_Composite->ui_icon = ICON_NODE_COMPOSITING; + ntreeType_Shader->ui_icon = ICON_NODE_MATERIAL; + ntreeType_Texture->ui_icon = ICON_NODE_TEXTURE; + ntreeType_Geometry->ui_icon = ICON_NODETREE; +} + +void ED_init_custom_node_type(bNodeType *ntype) +{ + /* default ui functions */ + ntype->draw_nodetype = node_draw_default; + ntype->draw_nodetype_prepare = node_update_default; + ntype->resize_area_func = node_resize_area_default; + ntype->select_area_func = node_select_area_default; + ntype->tweak_area_func = node_tweak_area_default; +} + +void ED_init_custom_node_socket_type(bNodeSocketType *stype) +{ + /* default ui functions */ + stype->draw = node_socket_button_label; +} + +static const float virtual_node_socket_color[4] = {0.2, 0.2, 0.2, 1.0}; + +/* maps standard socket integer type to a color */ +static const float std_node_socket_colors[][4] = { + {0.63, 0.63, 0.63, 1.0}, /* SOCK_FLOAT */ + {0.39, 0.39, 0.78, 1.0}, /* SOCK_VECTOR */ + {0.78, 0.78, 0.16, 1.0}, /* SOCK_RGBA */ + {0.39, 0.78, 0.39, 1.0}, /* SOCK_SHADER */ + {0.80, 0.65, 0.84, 1.0}, /* SOCK_BOOLEAN */ + {0.0, 0.0, 0.0, 1.0}, /*__SOCK_MESH (deprecated) */ + {0.35, 0.55, 0.36, 1.0}, /* SOCK_INT */ + {0.44, 0.70, 1.00, 1.0}, /* SOCK_STRING */ + {0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */ + {0.39, 0.22, 0.39, 1.0}, /* SOCK_IMAGE */ + {0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */ + {0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */ + {0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */ + {0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */ +}; + +/* common color callbacks for standard types */ +static void std_node_socket_draw_color(bContext *UNUSED(C), + PointerRNA *ptr, + PointerRNA *UNUSED(node_ptr), + float *r_color) +{ + bNodeSocket *sock = (bNodeSocket *)ptr->data; + int type = sock->typeinfo->type; + copy_v4_v4(r_color, std_node_socket_colors[type]); +} +static void std_node_socket_interface_draw_color(bContext *UNUSED(C), + PointerRNA *ptr, + float *r_color) +{ + bNodeSocket *sock = (bNodeSocket *)ptr->data; + int type = sock->typeinfo->type; + copy_v4_v4(r_color, std_node_socket_colors[type]); +} + +/* draw function for file output node sockets, + * displays only sub-path and format, no value button */ +static void node_file_output_socket_draw(bContext *C, + uiLayout *layout, + PointerRNA *ptr, + PointerRNA *node_ptr) +{ + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + bNodeSocket *sock = (bNodeSocket *)ptr->data; + uiLayout *row; + PointerRNA inputptr; + + row = uiLayoutRow(layout, false); + + PointerRNA imfptr = RNA_pointer_get(node_ptr, "format"); + int imtype = RNA_enum_get(&imfptr, "file_format"); + + if (imtype == R_IMF_IMTYPE_MULTILAYER) { + NodeImageMultiFileSocket *input = (NodeImageMultiFileSocket *)sock->storage; + RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr); + + uiItemL(row, input->layer, ICON_NONE); + } + else { + NodeImageMultiFileSocket *input = (NodeImageMultiFileSocket *)sock->storage; + uiBlock *block; + RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotFile, input, &inputptr); + + uiItemL(row, input->path, ICON_NONE); + + if (!RNA_boolean_get(&inputptr, "use_node_format")) { + imfptr = RNA_pointer_get(&inputptr, "format"); + } + + const char *imtype_name; + PropertyRNA *imtype_prop = RNA_struct_find_property(&imfptr, "file_format"); + RNA_property_enum_name((bContext *)C, + &imfptr, + imtype_prop, + RNA_property_enum_get(&imfptr, imtype_prop), + &imtype_name); + block = uiLayoutGetBlock(row); + UI_block_emboss_set(block, UI_EMBOSS_PULLDOWN); + uiItemL(row, imtype_name, ICON_NONE); + UI_block_emboss_set(block, UI_EMBOSS_NONE); + } +} + +static void std_node_socket_draw( + bContext *C, uiLayout *layout, PointerRNA *ptr, PointerRNA *node_ptr, const char *text) +{ + bNode *node = (bNode *)node_ptr->data; + bNodeSocket *sock = (bNodeSocket *)ptr->data; + int type = sock->typeinfo->type; + /*int subtype = sock->typeinfo->subtype;*/ + + /* XXX not nice, eventually give this node its own socket type ... */ + if (node->type == CMP_NODE_OUTPUT_FILE) { + node_file_output_socket_draw(C, layout, ptr, node_ptr); + return; + } + + if ((sock->in_out == SOCK_OUT) || (sock->flag & SOCK_IN_USE) || (sock->flag & SOCK_HIDE_VALUE)) { + node_socket_button_label(C, layout, ptr, node_ptr, text); + return; + } + + switch (type) { + case SOCK_FLOAT: + case SOCK_INT: + case SOCK_BOOLEAN: + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + break; + case SOCK_VECTOR: + if (sock->flag & SOCK_COMPACT) { + uiTemplateComponentMenu(layout, ptr, "default_value", text); + } + else { + if (sock->typeinfo->subtype == PROP_DIRECTION) { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE); + } + else { + uiLayout *column = uiLayoutColumn(layout, true); + uiItemR(column, ptr, "default_value", DEFAULT_FLAGS, text, ICON_NONE); + } + } + break; + case SOCK_RGBA: { + uiLayout *row = uiLayoutSplit(layout, 0.4f, false); + uiItemL(row, text, 0); + uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); + break; + } + case SOCK_STRING: { + uiLayout *row = uiLayoutSplit(layout, 0.4f, false); + uiItemL(row, text, 0); + + const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; + if (node_tree->type == NTREE_GEOMETRY) { + node_geometry_add_attribute_search_button(C, node_tree, node, ptr, row); + } + else { + uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); + } + + break; + } + case SOCK_OBJECT: { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + break; + } + case SOCK_IMAGE: { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + break; + } + case SOCK_COLLECTION: { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + break; + } + case SOCK_TEXTURE: { + uiTemplateID( + layout, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); + break; + } + case SOCK_MATERIAL: { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + break; + } + default: + node_socket_button_label(C, layout, ptr, node_ptr, text); + break; + } +} + +static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout, PointerRNA *ptr) +{ + bNodeSocket *sock = (bNodeSocket *)ptr->data; + int type = sock->typeinfo->type; + + uiLayout *col = uiLayoutColumn(layout, false); + + switch (type) { + case SOCK_FLOAT: { + uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiLayout *sub = uiLayoutColumn(col, true); + uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + break; + } + case SOCK_INT: { + uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiLayout *sub = uiLayoutColumn(col, true); + uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + break; + } + case SOCK_VECTOR: { + uiItemR(col, ptr, "default_value", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE); + uiLayout *sub = uiLayoutColumn(col, true); + uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + break; + } + case SOCK_BOOLEAN: + case SOCK_RGBA: + case SOCK_STRING: { + uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), 0); + break; + } + } + + uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, nullptr, 0); +} + +void ED_init_standard_node_socket_type(bNodeSocketType *stype) +{ + stype->draw = std_node_socket_draw; + stype->draw_color = std_node_socket_draw_color; + stype->interface_draw = std_node_socket_interface_draw; + stype->interface_draw_color = std_node_socket_interface_draw_color; +} + +static void node_socket_virtual_draw_color(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PointerRNA *UNUSED(node_ptr), + float *r_color) +{ + copy_v4_v4(r_color, virtual_node_socket_color); +} + +void ED_init_node_socket_type_virtual(bNodeSocketType *stype) +{ + stype->draw = node_socket_button_label; + stype->draw_color = node_socket_virtual_draw_color; +} + +/* ************** Generic drawing ************** */ + +void draw_nodespace_back_pix(const bContext *C, + ARegion *region, + SpaceNode *snode, + bNodeInstanceKey parent_key) +{ + Main *bmain = CTX_data_main(C); + bNodeInstanceKey active_viewer_key = (snode->nodetree ? snode->nodetree->active_viewer_key : + NODE_INSTANCE_KEY_NONE); + GPU_matrix_push_projection(); + GPU_matrix_push(); + wmOrtho2_region_pixelspace(region); + GPU_matrix_identity_set(); + ED_region_draw_cb_draw(C, region, REGION_DRAW_BACKDROP); + GPU_matrix_pop_projection(); + GPU_matrix_pop(); + + if (!(snode->flag & SNODE_BACKDRAW) || !ED_node_is_compositor(snode)) { + return; + } + + if (parent_key.value != active_viewer_key.value) { + return; + } + + GPU_matrix_push_projection(); + GPU_matrix_push(); + + /* The draw manager is used to draw the backdrop image. */ + GPUFrameBuffer *old_fb = GPU_framebuffer_active_get(); + GPU_framebuffer_restore(); + BLI_thread_lock(LOCK_DRAW_IMAGE); + DRW_draw_view(C); + BLI_thread_unlock(LOCK_DRAW_IMAGE); + GPU_framebuffer_bind_no_srgb(old_fb); + /* Draw manager changes the depth state. Set it back to NONE. Without this the node preview + * images aren't drawn correctly. */ + GPU_depth_test(GPU_DEPTH_NONE); + + void *lock; + Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); + if (ibuf) { + /* somehow the offset has to be calculated inverse */ + wmOrtho2_region_pixelspace(region); + const float x = (region->winx - snode->zoom * ibuf->x) / 2 + snode->xof; + const float y = (region->winy - snode->zoom * ibuf->y) / 2 + snode->yof; + + /** \note draw selected info on backdrop */ + if (snode->edittree) { + bNode *node = (bNode *)snode->edittree->nodes.first; + rctf *viewer_border = &snode->nodetree->viewer_border; + while (node) { + if (node->flag & NODE_SELECT) { + if (node->typeinfo->draw_backdrop) { + node->typeinfo->draw_backdrop(snode, ibuf, node, x, y); + } + } + node = node->next; + } + + if ((snode->nodetree->flag & NTREE_VIEWER_BORDER) && + viewer_border->xmin < viewer_border->xmax && viewer_border->ymin < viewer_border->ymax) { + rcti pixel_border; + BLI_rcti_init(&pixel_border, + x + snode->zoom * viewer_border->xmin * ibuf->x, + x + snode->zoom * viewer_border->xmax * ibuf->x, + y + snode->zoom * viewer_border->ymin * ibuf->y, + y + snode->zoom * viewer_border->ymax * ibuf->y); + + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColor(TH_ACTIVE); + + immDrawBorderCorners(pos, &pixel_border, 1.0f, 1.0f); + + immUnbindProgram(); + } + } + } + + BKE_image_release_ibuf(ima, ibuf, lock); + GPU_matrix_pop_projection(); + GPU_matrix_pop(); +} + +/* return quadratic beziers points for a given nodelink and clip if v2d is not nullptr. */ +bool node_link_bezier_handles(const View2D *v2d, + const SpaceNode *snode, + const bNodeLink *link, + float vec[4][2]) +{ + float cursor[2] = {0.0f, 0.0f}; + + /* this function can be called with snode null (via cut_links_intersect) */ + /* XXX map snode->runtime->cursor back to view space */ + if (snode) { + cursor[0] = snode->runtime->cursor[0] * UI_DPI_FAC; + cursor[1] = snode->runtime->cursor[1] * UI_DPI_FAC; + } + + /* in v0 and v3 we put begin/end points */ + int toreroute, fromreroute; + if (link->fromsock) { + vec[0][0] = link->fromsock->locx; + vec[0][1] = link->fromsock->locy; + if (link->fromsock->flag & SOCK_MULTI_INPUT) { + node_link_calculate_multi_input_position(link->fromsock->locx, + link->fromsock->locy, + link->fromsock->total_inputs - 1, + link->fromsock->total_inputs, + vec[0]); + } + fromreroute = (link->fromnode && link->fromnode->type == NODE_REROUTE); + } + else { + if (snode == nullptr) { + return false; + } + copy_v2_v2(vec[0], cursor); + fromreroute = 0; + } + if (link->tosock) { + vec[3][0] = link->tosock->locx; + vec[3][1] = link->tosock->locy; + if (!(link->tonode->flag & NODE_HIDDEN) && link->tosock->flag & SOCK_MULTI_INPUT) { + node_link_calculate_multi_input_position(link->tosock->locx, + link->tosock->locy, + link->multi_input_socket_index, + link->tosock->total_inputs, + vec[3]); + } + toreroute = (link->tonode && link->tonode->type == NODE_REROUTE); + } + else { + if (snode == nullptr) { + return false; + } + copy_v2_v2(vec[3], cursor); + toreroute = 0; + } + + /* may be called outside of drawing (so pass spacetype) */ + int curving = UI_GetThemeValueType(TH_NODE_CURVING, SPACE_NODE); + + if (curving == 0) { + /* Straight line: align all points. */ + mid_v2_v2v2(vec[1], vec[0], vec[3]); + mid_v2_v2v2(vec[2], vec[1], vec[3]); + return true; + } + + const float dist = curving * 0.10f * fabsf(vec[0][0] - vec[3][0]); + const float deltax = vec[3][0] - vec[0][0]; + const float deltay = vec[3][1] - vec[0][1]; + /* check direction later, for top sockets */ + if (fromreroute) { + if (fabsf(deltax) > fabsf(deltay)) { + vec[1][1] = vec[0][1]; + vec[1][0] = vec[0][0] + (deltax > 0 ? dist : -dist); + } + else { + vec[1][0] = vec[0][0]; + vec[1][1] = vec[0][1] + (deltay > 0 ? dist : -dist); + } + } + else { + vec[1][0] = vec[0][0] + dist; + vec[1][1] = vec[0][1]; + } + if (toreroute) { + if (fabsf(deltax) > fabsf(deltay)) { + vec[2][1] = vec[3][1]; + vec[2][0] = vec[3][0] + (deltax > 0 ? -dist : dist); + } + else { + vec[2][0] = vec[3][0]; + vec[2][1] = vec[3][1] + (deltay > 0 ? -dist : dist); + } + } + else { + vec[2][0] = vec[3][0] - dist; + vec[2][1] = vec[3][1]; + } + + if (v2d && min_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) > v2d->cur.xmax) { + return false; /* clipped */ + } + if (v2d && max_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) < v2d->cur.xmin) { + return false; /* clipped */ + } + + return true; +} + +/* if v2d not nullptr, it clips and returns 0 if not visible */ +bool node_link_bezier_points(const View2D *v2d, + const SpaceNode *snode, + const bNodeLink *link, + float coord_array[][2], + const int resol) +{ + float vec[4][2]; + + if (node_link_bezier_handles(v2d, snode, link, vec)) { + /* always do all three, to prevent data hanging around */ + BKE_curve_forward_diff_bezier( + vec[0][0], vec[1][0], vec[2][0], vec[3][0], coord_array[0] + 0, resol, sizeof(float[2])); + BKE_curve_forward_diff_bezier( + vec[0][1], vec[1][1], vec[2][1], vec[3][1], coord_array[0] + 1, resol, sizeof(float[2])); + + return true; + } + return false; +} + +#define NODELINK_GROUP_SIZE 256 +#define LINK_RESOL 24 +#define LINK_WIDTH (2.5f * UI_DPI_FAC) +#define ARROW_SIZE (7 * UI_DPI_FAC) + +/* Reroute arrow shape and mute bar. These are expanded here and shrunk in the glsl code. + * See: gpu_shader_2D_nodelink_vert.glsl */ +static float arrow_verts[3][2] = {{-1.0f, 1.0f}, {0.0f, 0.0f}, {-1.0f, -1.0f}}; +static float arrow_expand_axis[3][2] = {{0.7071f, 0.7071f}, {M_SQRT2, 0.0f}, {0.7071f, -0.7071f}}; +static float mute_verts[3][2] = {{0.7071f, 1.0f}, {0.7071f, 0.0f}, {0.7071f, -1.0f}}; +static float mute_expand_axis[3][2] = {{1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, -0.0f}}; + +/* Is zero initialized because it is static data. */ +static struct { + GPUBatch *batch; /* for batching line together */ + GPUBatch *batch_single; /* for single line */ + GPUVertBuf *inst_vbo; + uint p0_id, p1_id, p2_id, p3_id; + uint colid_id, muted_id; + GPUVertBufRaw p0_step, p1_step, p2_step, p3_step; + GPUVertBufRaw colid_step, muted_step; + uint count; + bool enabled; +} g_batch_link; + +static void nodelink_batch_reset() +{ + GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p0_id, &g_batch_link.p0_step); + GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p1_id, &g_batch_link.p1_step); + GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p2_id, &g_batch_link.p2_step); + GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p3_id, &g_batch_link.p3_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.colid_id, &g_batch_link.colid_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.muted_id, &g_batch_link.muted_step); + g_batch_link.count = 0; +} + +static void set_nodelink_vertex(GPUVertBuf *vbo, + uint uv_id, + uint pos_id, + uint exp_id, + uint v, + const uchar uv[2], + const float pos[2], + const float exp[2]) +{ + GPU_vertbuf_attr_set(vbo, uv_id, v, uv); + GPU_vertbuf_attr_set(vbo, pos_id, v, pos); + GPU_vertbuf_attr_set(vbo, exp_id, v, exp); +} + +static void nodelink_batch_init() +{ + GPUVertFormat format = {0}; + uint uv_id = GPU_vertformat_attr_add(&format, "uv", GPU_COMP_U8, 2, GPU_FETCH_INT_TO_FLOAT_UNIT); + uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint expand_id = GPU_vertformat_attr_add(&format, "expand", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPUVertBuf *vbo = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_STATIC); + int vcount = LINK_RESOL * 2; /* curve */ + vcount += 2; /* restart strip */ + vcount += 3 * 2; /* arrow */ + vcount += 2; /* restart strip */ + vcount += 3 * 2; /* mute */ + vcount *= 2; /* shadow */ + vcount += 2; /* restart strip */ + GPU_vertbuf_data_alloc(vbo, vcount); + int v = 0; + + for (int k = 0; k < 2; k++) { + uchar uv[2] = {0, 0}; + float pos[2] = {0.0f, 0.0f}; + float exp[2] = {0.0f, 1.0f}; + + /* restart */ + if (k == 1) { + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + } + + /* curve strip */ + for (int i = 0; i < LINK_RESOL; i++) { + uv[0] = 255 * (i / (float)(LINK_RESOL - 1)); + uv[1] = 0; + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + uv[1] = 255; + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + } + /* restart */ + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + + uv[0] = 127; + uv[1] = 0; + copy_v2_v2(pos, arrow_verts[0]); + copy_v2_v2(exp, arrow_expand_axis[0]); + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + /* arrow */ + for (int i = 0; i < 3; i++) { + uv[1] = 0; + copy_v2_v2(pos, arrow_verts[i]); + copy_v2_v2(exp, arrow_expand_axis[i]); + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + + uv[1] = 255; + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + } + + /* restart */ + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + + uv[0] = 127; + uv[1] = 0; + copy_v2_v2(pos, mute_verts[0]); + copy_v2_v2(exp, mute_expand_axis[0]); + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + /* bar */ + for (int i = 0; i < 3; ++i) { + uv[1] = 0; + copy_v2_v2(pos, mute_verts[i]); + copy_v2_v2(exp, mute_expand_axis[i]); + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + + uv[1] = 255; + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + } + + /* restart */ + if (k == 0) { + set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp); + } + } + + g_batch_link.batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_OWNS_VBO); + gpu_batch_presets_register(g_batch_link.batch); + + g_batch_link.batch_single = GPU_batch_create_ex( + GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_INVALID); + gpu_batch_presets_register(g_batch_link.batch_single); + + /* Instances data */ + GPUVertFormat format_inst = {0}; + g_batch_link.p0_id = GPU_vertformat_attr_add( + &format_inst, "P0", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + g_batch_link.p1_id = GPU_vertformat_attr_add( + &format_inst, "P1", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + g_batch_link.p2_id = GPU_vertformat_attr_add( + &format_inst, "P2", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + g_batch_link.p3_id = GPU_vertformat_attr_add( + &format_inst, "P3", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + g_batch_link.colid_id = GPU_vertformat_attr_add( + &format_inst, "colid_doarrow", GPU_COMP_U8, 4, GPU_FETCH_INT); + g_batch_link.muted_id = GPU_vertformat_attr_add( + &format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT); + g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM); + /* Alloc max count but only draw the range we need. */ + GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE); + + GPU_batch_instbuf_set(g_batch_link.batch, g_batch_link.inst_vbo, true); + + nodelink_batch_reset(); +} + +static char nodelink_get_color_id(int th_col) +{ + switch (th_col) { + case TH_WIRE: + return 1; + case TH_WIRE_INNER: + return 2; + case TH_ACTIVE: + return 3; + case TH_EDGE_SELECT: + return 4; + case TH_REDALERT: + return 5; + } + return 0; +} + +static void nodelink_batch_draw(const SpaceNode *snode) +{ + if (g_batch_link.count == 0) { + return; + } + + GPU_blend(GPU_BLEND_ALPHA); + + float colors[6][4] = {{0.0f}}; + UI_GetThemeColor4fv(TH_WIRE_INNER, colors[nodelink_get_color_id(TH_WIRE_INNER)]); + UI_GetThemeColor4fv(TH_WIRE, colors[nodelink_get_color_id(TH_WIRE)]); + UI_GetThemeColor4fv(TH_ACTIVE, colors[nodelink_get_color_id(TH_ACTIVE)]); + UI_GetThemeColor4fv(TH_EDGE_SELECT, colors[nodelink_get_color_id(TH_EDGE_SELECT)]); + UI_GetThemeColor4fv(TH_REDALERT, colors[nodelink_get_color_id(TH_REDALERT)]); + + GPU_vertbuf_data_len_set(g_batch_link.inst_vbo, g_batch_link.count); + GPU_vertbuf_use(g_batch_link.inst_vbo); /* force update. */ + + GPU_batch_program_set_builtin(g_batch_link.batch, GPU_SHADER_2D_NODELINK_INST); + GPU_batch_uniform_4fv_array(g_batch_link.batch, "colors", 6, colors); + GPU_batch_uniform_1f(g_batch_link.batch, "expandSize", snode->runtime->aspect * LINK_WIDTH); + GPU_batch_uniform_1f(g_batch_link.batch, "arrowSize", ARROW_SIZE); + GPU_batch_draw(g_batch_link.batch); + + nodelink_batch_reset(); + + GPU_blend(GPU_BLEND_NONE); +} + +void nodelink_batch_start(SpaceNode *UNUSED(snode)) +{ + g_batch_link.enabled = true; +} + +void nodelink_batch_end(SpaceNode *snode) +{ + nodelink_batch_draw(snode); + g_batch_link.enabled = false; +} + +static void nodelink_batch_add_link(const SpaceNode *snode, + const float p0[2], + const float p1[2], + const float p2[2], + const float p3[2], + int th_col1, + int th_col2, + int th_col3, + bool drawarrow, + bool drawmuted) +{ + /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ + BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); + BLI_assert(ELEM(th_col2, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); + BLI_assert(ELEM(th_col3, TH_WIRE, TH_REDALERT, -1)); + + g_batch_link.count++; + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p1_step), p1); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p2_step), p2); + copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p3_step), p3); + char *colid = (char *)GPU_vertbuf_raw_step(&g_batch_link.colid_step); + colid[0] = nodelink_get_color_id(th_col1); + colid[1] = nodelink_get_color_id(th_col2); + colid[2] = nodelink_get_color_id(th_col3); + colid[3] = drawarrow; + char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step); + muted[0] = drawmuted; + + if (g_batch_link.count == NODELINK_GROUP_SIZE) { + nodelink_batch_draw(snode); + } +} + +/* don't do shadows if th_col3 is -1. */ +void node_draw_link_bezier(const View2D *v2d, + const SpaceNode *snode, + const bNodeLink *link, + int th_col1, + int th_col2, + int th_col3) +{ + float vec[4][2]; + const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT; + if (node_link_bezier_handles(v2d, snode, link, vec)) { + int drawarrow = ((link->tonode && (link->tonode->type == NODE_REROUTE)) && + (link->fromnode && (link->fromnode->type == NODE_REROUTE))); + int drawmuted = (link->flag & NODE_LINK_MUTED); + if (g_batch_link.batch == nullptr) { + nodelink_batch_init(); + } + + if (g_batch_link.enabled && !highlighted) { + /* Add link to batch. */ + nodelink_batch_add_link( + snode, vec[0], vec[1], vec[2], vec[3], th_col1, th_col2, th_col3, drawarrow, drawmuted); + } + else { + /* Draw single link. */ + float colors[3][4] = {{0.0f}}; + if (th_col3 != -1) { + UI_GetThemeColor4fv(th_col3, colors[0]); + } + UI_GetThemeColor4fv(th_col1, colors[1]); + UI_GetThemeColor4fv(th_col2, colors[2]); + + if (highlighted) { + float link_preselection_highlight_color[4]; + UI_GetThemeColor4fv(TH_SELECT, link_preselection_highlight_color); + copy_v4_v4(colors[2], link_preselection_highlight_color); + } + + GPUBatch *batch = g_batch_link.batch_single; + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK); + GPU_batch_uniform_2fv_array(batch, "bezierPts", 4, vec); + GPU_batch_uniform_4fv_array(batch, "colors", 3, colors); + GPU_batch_uniform_1f(batch, "expandSize", snode->runtime->aspect * LINK_WIDTH); + GPU_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE); + GPU_batch_uniform_1i(batch, "doArrow", drawarrow); + GPU_batch_uniform_1i(batch, "doMuted", drawmuted); + GPU_batch_draw(batch); + } + } +} + +/* note; this is used for fake links in groups too */ +void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) +{ + int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE; + + if (link->fromsock == nullptr && link->tosock == nullptr) { + return; + } + + /* new connection */ + if (!link->fromsock || !link->tosock) { + th_col1 = th_col2 = TH_ACTIVE; + } + else { + /* going to give issues once... */ + if (link->tosock->flag & SOCK_UNAVAIL) { + return; + } + if (link->fromsock->flag & SOCK_UNAVAIL) { + return; + } + + if (link->flag & NODE_LINK_VALID) { + /* special indicated link, on drop-node */ + if (link->flag & NODE_LINKFLAG_HILITE) { + th_col1 = th_col2 = TH_ACTIVE; + } + else if (link->flag & NODE_LINK_MUTED) { + th_col1 = th_col2 = TH_REDALERT; + } + else { + /* Regular link, highlight if connected to selected node. */ + if (link->fromnode && link->fromnode->flag & SELECT) { + th_col1 = TH_EDGE_SELECT; + } + if (link->tonode && link->tonode->flag & SELECT) { + th_col2 = TH_EDGE_SELECT; + } + } + } + else { + /* Invalid link. */ + th_col1 = th_col2 = th_col3 = TH_REDALERT; + // th_col3 = -1; /* no shadow */ + } + } + + node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3); +} + +void ED_node_draw_snap(View2D *v2d, const float cent[2], float size, NodeBorder border, uint pos) +{ + immBegin(GPU_PRIM_LINES, 4); + + if (border & (NODE_LEFT | NODE_RIGHT)) { + immVertex2f(pos, cent[0], v2d->cur.ymin); + immVertex2f(pos, cent[0], v2d->cur.ymax); + } + else { + immVertex2f(pos, cent[0], cent[1] - size); + immVertex2f(pos, cent[0], cent[1] + size); + } + + if (border & (NODE_TOP | NODE_BOTTOM)) { + immVertex2f(pos, v2d->cur.xmin, cent[1]); + immVertex2f(pos, v2d->cur.xmax, cent[1]); + } + else { + immVertex2f(pos, cent[0] - size, cent[1]); + immVertex2f(pos, cent[0] + size, cent[1]); + } + + immEnd(); +} diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.c deleted file mode 100644 index a28d467df2e..00000000000 --- a/source/blender/editors/space_node/node_add.c +++ /dev/null @@ -1,1019 +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. - */ - -/** \file - * \ingroup spnode - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_collection_types.h" -#include "DNA_node_types.h" -#include "DNA_texture_types.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - -#include "BLT_translation.h" - -#include "BKE_context.h" -#include "BKE_image.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_node.h" -#include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_texture.h" - -#include "DEG_depsgraph_build.h" - -#include "ED_node.h" /* own include */ -#include "ED_render.h" -#include "ED_screen.h" - -#include "RNA_access.h" -#include "RNA_define.h" -#include "RNA_enum_types.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "UI_view2d.h" - -#include "node_intern.h" /* own include */ - -/* -------------------------------------------------------------------- */ -/** \name Utilities - * \{ */ - -/** - * XXX Does some additional initialization on top of #nodeAddNode - * Can be used with both custom and static nodes, - * if `idname == NULL` the static int type will be used instead. - */ -bNode *node_add_node(const bContext *C, const char *idname, int type, float locx, float locy) -{ - SpaceNode *snode = CTX_wm_space_node(C); - Main *bmain = CTX_data_main(C); - bNode *node = NULL; - - node_deselect_all(snode); - - if (idname) { - node = nodeAddNode(C, snode->edittree, idname); - } - else { - node = nodeAddStaticNode(C, snode->edittree, type); - } - BLI_assert(node && node->typeinfo); - - /* Position mouse in node header. */ - node->locx = locx - NODE_DY * 1.5f / UI_DPI_FAC; - node->locy = locy + NODE_DY * 0.5f / UI_DPI_FAC; - - nodeSetSelected(node, true); - - ntreeUpdateTree(bmain, snode->edittree); - ED_node_set_active(bmain, snode->edittree, node, NULL); - - snode_update(snode, node); - - if (snode->nodetree->type == NTREE_TEXTURE) { - ntreeTexCheckCyclics(snode->edittree); - } - - return node; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Add Reroute Operator - * \{ */ - -static bool add_reroute_intersect_check(bNodeLink *link, - float mcoords[][2], - int tot, - float result[2]) -{ - float coord_array[NODE_LINK_RESOL + 1][2]; - - if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) { - for (int i = 0; i < tot - 1; i++) { - for (int b = 0; b < NODE_LINK_RESOL; b++) { - if (isect_seg_seg_v2_point( - mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1], result) > 0) { - return true; - } - } - } - } - return false; -} - -typedef struct bNodeSocketLink { - struct bNodeSocketLink *next, *prev; - - struct bNodeSocket *sock; - struct bNodeLink *link; - float point[2]; -} bNodeSocketLink; - -static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb, - bNodeSocket *sock, - bNodeLink *link, - const float point[2]) -{ - bNodeSocketLink *socklink, *prev; - - socklink = MEM_callocN(sizeof(bNodeSocketLink), "socket link"); - socklink->sock = sock; - socklink->link = link; - copy_v2_v2(socklink->point, point); - - for (prev = lb->last; prev; prev = prev->prev) { - if (prev->sock == sock) { - break; - } - } - BLI_insertlinkafter(lb, prev, socklink); - return socklink; -} - -static bNodeSocketLink *add_reroute_do_socket_section(bContext *C, - bNodeSocketLink *socklink, - int in_out) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - bNode *reroute_node = NULL; - bNodeSocket *cursock = socklink->sock; - float insert_point[2]; - int num_links; - - zero_v2(insert_point); - num_links = 0; - - while (socklink && socklink->sock == cursock) { - if (!(socklink->link->flag & NODE_LINK_TEST)) { - socklink->link->flag |= NODE_LINK_TEST; - - /* create the reroute node for this cursock */ - if (!reroute_node) { - reroute_node = nodeAddStaticNode(C, ntree, NODE_REROUTE); - - /* add a single link to/from the reroute node to replace multiple links */ - if (in_out == SOCK_OUT) { - nodeAddLink(ntree, - socklink->link->fromnode, - socklink->link->fromsock, - reroute_node, - reroute_node->inputs.first); - } - else { - nodeAddLink(ntree, - reroute_node, - reroute_node->outputs.first, - socklink->link->tonode, - socklink->link->tosock); - } - } - - /* insert the reroute node into the link */ - if (in_out == SOCK_OUT) { - socklink->link->fromnode = reroute_node; - socklink->link->fromsock = reroute_node->outputs.first; - } - else { - socklink->link->tonode = reroute_node; - socklink->link->tosock = reroute_node->inputs.first; - } - - add_v2_v2(insert_point, socklink->point); - num_links++; - } - socklink = socklink->next; - } - - if (num_links > 0) { - /* average cut point from shared links */ - mul_v2_fl(insert_point, 1.0f / num_links); - - reroute_node->locx = insert_point[0] / UI_DPI_FAC; - reroute_node->locy = insert_point[1] / UI_DPI_FAC; - } - - return socklink; -} - -static int add_reroute_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - bNodeTree *ntree = snode->edittree; - float mcoords[256][2]; - int i = 0; - - /* Get the cut path */ - RNA_BEGIN (op->ptr, itemptr, "path") { - float loc[2]; - - RNA_float_get_array(&itemptr, "loc", loc); - UI_view2d_region_to_view( - ®ion->v2d, (short)loc[0], (short)loc[1], &mcoords[i][0], &mcoords[i][1]); - i++; - if (i >= 256) { - break; - } - } - RNA_END; - - if (i > 1) { - ListBase output_links, input_links; - bNodeLink *link; - bNodeSocketLink *socklink; - float insert_point[2]; - - /* always first */ - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - node_deselect_all(snode); - - /* Find cut links and sort them by sockets */ - BLI_listbase_clear(&output_links); - BLI_listbase_clear(&input_links); - - for (link = ntree->links.first; link; link = link->next) { - if (nodeLinkIsHidden(link)) { - continue; - } - if (add_reroute_intersect_check(link, mcoords, i, insert_point)) { - add_reroute_insert_socket_link(&output_links, link->fromsock, link, insert_point); - add_reroute_insert_socket_link(&input_links, link->tosock, link, insert_point); - - /* Clear flag */ - link->flag &= ~NODE_LINK_TEST; - } - } - - /* Create reroute nodes for intersected links. - * Only one reroute if links share the same input/output socket. - */ - socklink = output_links.first; - while (socklink) { - socklink = add_reroute_do_socket_section(C, socklink, SOCK_OUT); - } - socklink = input_links.first; - while (socklink) { - socklink = add_reroute_do_socket_section(C, socklink, SOCK_IN); - } - - BLI_freelistN(&output_links); - BLI_freelistN(&input_links); - - /* always last */ - ntreeUpdateTree(CTX_data_main(C), ntree); - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; - } - - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; -} - -void NODE_OT_add_reroute(wmOperatorType *ot) -{ - ot->name = "Add Reroute"; - ot->idname = "NODE_OT_add_reroute"; - ot->description = "Add a reroute node"; - - ot->invoke = WM_gesture_lines_invoke; - ot->modal = WM_gesture_lines_modal; - ot->exec = add_reroute_exec; - ot->cancel = WM_gesture_lines_cancel; - - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - PropertyRNA *prop; - prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - /* internal */ - RNA_def_int(ot->srna, "cursor", WM_CURSOR_CROSS, 0, INT_MAX, "Cursor", "", 0, INT_MAX); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Add Node Group Operator - * \{ */ - -static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain, - wmOperator *op, - bNodeTree *ntree) -{ - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - - bNodeTree *node_group = (bNodeTree *)BKE_libblock_find_name(bmain, ID_NT, name); - if (!node_group) { - return NULL; - } - - const char *disabled_hint = NULL; - if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group, &disabled_hint)) { - if (disabled_hint) { - BKE_reportf(op->reports, - RPT_ERROR, - "Can not add node group '%s' to '%s':\n %s", - node_group->id.name + 2, - ntree->id.name + 2, - disabled_hint); - } - else { - BKE_reportf(op->reports, - RPT_ERROR, - "Can not add node group '%s' to '%s'", - node_group->id.name + 2, - ntree->id.name + 2); - } - - return NULL; - } - - return node_group; -} - -static int node_add_group_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - bNodeTree *node_group; - - if (!(node_group = node_add_group_get_and_poll_group_node_tree(bmain, op, ntree))) { - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - bNode *group_node = node_add_node(C, - node_group_idname(C), - (node_group->type == NTREE_CUSTOM) ? NODE_CUSTOM_GROUP : - NODE_GROUP, - snode->runtime->cursor[0], - snode->runtime->cursor[1]); - if (!group_node) { - BKE_report(op->reports, RPT_WARNING, "Could not add node group"); - return OPERATOR_CANCELLED; - } - - group_node->id = &node_group->id; - id_us_plus(group_node->id); - - nodeSetActive(ntree, group_node); - ntreeUpdateTree(bmain, node_group); - ntreeUpdateTree(bmain, ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -static int node_add_group_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - - /* Convert mouse coordinates to v2d space. */ - UI_view2d_region_to_view(®ion->v2d, - event->mval[0], - event->mval[1], - &snode->runtime->cursor[0], - &snode->runtime->cursor[1]); - - snode->runtime->cursor[0] /= UI_DPI_FAC; - snode->runtime->cursor[1] /= UI_DPI_FAC; - - return node_add_group_exec(C, op); -} - -void NODE_OT_add_group(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Node Group"; - ot->description = "Add an existing node group to the current node editor"; - ot->idname = "NODE_OT_add_group"; - - /* callbacks */ - ot->exec = node_add_group_exec; - ot->invoke = node_add_group_invoke; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Add Node Object Operator - * \{ */ - -static Object *node_add_object_get_and_poll_object_node_tree(Main *bmain, wmOperator *op) -{ - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - - Object *object = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); - if (!object) { - return NULL; - } - - return object; -} - -static int node_add_object_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - Object *object; - - if (!(object = node_add_object_get_and_poll_object_node_tree(bmain, op))) { - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - bNode *object_node = node_add_node( - C, NULL, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]); - if (!object_node) { - BKE_report(op->reports, RPT_WARNING, "Could not add node object"); - return OPERATOR_CANCELLED; - } - - bNodeSocket *sock = nodeFindSocket(object_node, SOCK_IN, "Object"); - if (!sock) { - BKE_report(op->reports, RPT_WARNING, "Could not find node object socket"); - return OPERATOR_CANCELLED; - } - - bNodeSocketValueObject *socket_data = sock->default_value; - socket_data->value = object; - id_us_plus(&object->id); - - nodeSetActive(ntree, object_node); - ntreeUpdateTree(bmain, ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - ED_node_tag_update_nodetree(bmain, ntree, object_node); - DEG_relations_tag_update(bmain); - - return OPERATOR_FINISHED; -} - -static int node_add_object_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - - /* Convert mouse coordinates to v2d space. */ - UI_view2d_region_to_view(®ion->v2d, - event->mval[0], - event->mval[1], - &snode->runtime->cursor[0], - &snode->runtime->cursor[1]); - - snode->runtime->cursor[0] /= UI_DPI_FAC; - snode->runtime->cursor[1] /= UI_DPI_FAC; - - return node_add_object_exec(C, op); -} - -static bool node_add_object_poll(bContext *C) -{ - const SpaceNode *snode = CTX_wm_space_node(C); - return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) && - !UI_but_active_drop_name(C); -} - -void NODE_OT_add_object(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Node Object"; - ot->description = "Add an object info node to the current node editor"; - ot->idname = "NODE_OT_add_object"; - - /* callbacks */ - ot->exec = node_add_object_exec; - ot->invoke = node_add_object_invoke; - ot->poll = node_add_object_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - RNA_def_string(ot->srna, "name", "Object", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Add Node Texture Operator - * \{ */ - -static Tex *node_add_texture_get_and_poll_texture_node_tree(Main *bmain, wmOperator *op) -{ - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - - Tex *texture = (Tex *)BKE_libblock_find_name(bmain, ID_TE, name); - if (!texture) { - return NULL; - } - - return texture; -} - -static int node_add_texture_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - Tex *texture; - - if (!(texture = node_add_texture_get_and_poll_texture_node_tree(bmain, op))) { - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - bNode *texture_node = node_add_node(C, - NULL, - GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, - snode->runtime->cursor[0], - snode->runtime->cursor[1]); - if (!texture_node) { - BKE_report(op->reports, RPT_WARNING, "Could not add texture node"); - return OPERATOR_CANCELLED; - } - - texture_node->id = &texture->id; - id_us_plus(&texture->id); - - nodeSetActive(ntree, texture_node); - ntreeUpdateTree(bmain, ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - ED_node_tag_update_nodetree(bmain, ntree, texture_node); - - return OPERATOR_FINISHED; -} - -static int node_add_texture_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - - /* Convert mouse coordinates to v2d space. */ - UI_view2d_region_to_view(®ion->v2d, - event->mval[0], - event->mval[1], - &snode->runtime->cursor[0], - &snode->runtime->cursor[1]); - - snode->runtime->cursor[0] /= UI_DPI_FAC; - snode->runtime->cursor[1] /= UI_DPI_FAC; - - return node_add_texture_exec(C, op); -} - -static bool node_add_texture_poll(bContext *C) -{ - const SpaceNode *snode = CTX_wm_space_node(C); - return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) && - !UI_but_active_drop_name(C); -} - -void NODE_OT_add_texture(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Node Texture"; - ot->description = "Add a texture to the current node editor"; - ot->idname = "NODE_OT_add_texture"; - - /* callbacks */ - ot->exec = node_add_texture_exec; - ot->invoke = node_add_texture_invoke; - ot->poll = node_add_texture_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - RNA_def_string( - ot->srna, "name", "Texture", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Add Node Collection Operator - * \{ */ - -static Collection *node_add_collection_get_and_poll_collection_node_tree(Main *bmain, - wmOperator *op) -{ - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - - Collection *collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); - if (!collection) { - return NULL; - } - - return collection; -} - -static int node_add_collection_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - Collection *collection; - - if (!(collection = node_add_collection_get_and_poll_collection_node_tree(bmain, op))) { - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - bNode *collection_node = node_add_node( - C, NULL, GEO_NODE_COLLECTION_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]); - if (!collection_node) { - BKE_report(op->reports, RPT_WARNING, "Could not add node collection"); - return OPERATOR_CANCELLED; - } - - bNodeSocket *sock = nodeFindSocket(collection_node, SOCK_IN, "Collection"); - if (!sock) { - BKE_report(op->reports, RPT_WARNING, "Could not find node collection socket"); - return OPERATOR_CANCELLED; - } - - bNodeSocketValueCollection *socket_data = sock->default_value; - socket_data->value = collection; - id_us_plus(&collection->id); - - nodeSetActive(ntree, collection_node); - ntreeUpdateTree(bmain, ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - ED_node_tag_update_nodetree(bmain, ntree, collection_node); - - return OPERATOR_FINISHED; -} - -static int node_add_collection_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - - /* Convert mouse coordinates to v2d space. */ - UI_view2d_region_to_view(®ion->v2d, - event->mval[0], - event->mval[1], - &snode->runtime->cursor[0], - &snode->runtime->cursor[1]); - - snode->runtime->cursor[0] /= UI_DPI_FAC; - snode->runtime->cursor[1] /= UI_DPI_FAC; - - return node_add_collection_exec(C, op); -} - -static bool node_add_collection_poll(bContext *C) -{ - const SpaceNode *snode = CTX_wm_space_node(C); - return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) && - !UI_but_active_drop_name(C); -} - -void NODE_OT_add_collection(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Node Collection"; - ot->description = "Add an collection info node to the current node editor"; - ot->idname = "NODE_OT_add_collection"; - - /* callbacks */ - ot->exec = node_add_collection_exec; - ot->invoke = node_add_collection_invoke; - ot->poll = node_add_collection_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - RNA_def_string( - ot->srna, "name", "Collection", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Add File Node Operator - * \{ */ - -static bool node_add_file_poll(bContext *C) -{ - const SpaceNode *snode = CTX_wm_space_node(C); - return ED_operator_node_editable(C) && - ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT); -} - -static int node_add_file_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNode *node; - Image *ima; - int type = 0; - - ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM); - if (!ima) { - return OPERATOR_CANCELLED; - } - - switch (snode->nodetree->type) { - case NTREE_SHADER: - type = SH_NODE_TEX_IMAGE; - break; - case NTREE_TEXTURE: - type = TEX_NODE_IMAGE; - break; - case NTREE_COMPOSIT: - type = CMP_NODE_IMAGE; - break; - default: - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - node = node_add_node(C, NULL, type, snode->runtime->cursor[0], snode->runtime->cursor[1]); - - if (!node) { - BKE_report(op->reports, RPT_WARNING, "Could not add an image node"); - return OPERATOR_CANCELLED; - } - - node->id = (ID *)ima; - - /* When adding new image file via drag-drop we need to load imbuf in order - * to get proper image source. - */ - if (RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD); - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); - } - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -static int node_add_file_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - - /* convert mouse coordinates to v2d space */ - UI_view2d_region_to_view(®ion->v2d, - event->mval[0], - event->mval[1], - &snode->runtime->cursor[0], - &snode->runtime->cursor[1]); - - snode->runtime->cursor[0] /= UI_DPI_FAC; - snode->runtime->cursor[1] /= UI_DPI_FAC; - - if (RNA_struct_property_is_set(op->ptr, "filepath") || - RNA_struct_property_is_set(op->ptr, "name")) { - return node_add_file_exec(C, op); - } - return WM_operator_filesel(C, op, event); -} - -void NODE_OT_add_file(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add File Node"; - ot->description = "Add a file node to the current node editor"; - ot->idname = "NODE_OT_add_file"; - - /* callbacks */ - ot->exec = node_add_file_exec; - ot->invoke = node_add_file_invoke; - ot->poll = node_add_file_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - WM_operator_properties_filesel(ot, - FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, - FILE_SPECIAL, - FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, - FILE_DEFAULTDISPLAY, - FILE_SORT_DEFAULT); - RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Add Mask Node Operator - * \{ */ - -static bool node_add_mask_poll(bContext *C) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - return ED_operator_node_editable(C) && snode->nodetree->type == NTREE_COMPOSIT; -} - -static int node_add_mask_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNode *node; - ID *mask = NULL; - - /* check input variables */ - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - mask = BKE_libblock_find_name(bmain, ID_MSK, name); - if (!mask) { - BKE_reportf(op->reports, RPT_ERROR, "Mask '%s' not found", name); - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - node = node_add_node( - C, NULL, CMP_NODE_MASK, snode->runtime->cursor[0], snode->runtime->cursor[1]); - - if (!node) { - BKE_report(op->reports, RPT_WARNING, "Could not add a mask node"); - return OPERATOR_CANCELLED; - } - - node->id = mask; - id_us_plus(mask); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_add_mask(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Mask Node"; - ot->description = "Add a mask node to the current node editor"; - ot->idname = "NODE_OT_add_mask"; - - /* callbacks */ - ot->exec = node_add_mask_exec; - ot->poll = node_add_mask_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name New Node Tree Operator - * \{ */ - -static int new_node_tree_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - Main *bmain = CTX_data_main(C); - bNodeTree *ntree; - PointerRNA ptr, idptr; - PropertyRNA *prop; - const char *idname; - char treename_buf[MAX_ID_NAME - 2]; - const char *treename; - - if (RNA_struct_property_is_set(op->ptr, "type")) { - prop = RNA_struct_find_property(op->ptr, "type"); - RNA_property_enum_identifier(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &idname); - } - else if (snode) { - idname = snode->tree_idname; - } - else { - return OPERATOR_CANCELLED; - } - - if (RNA_struct_property_is_set(op->ptr, "name")) { - RNA_string_get(op->ptr, "name", treename_buf); - treename = treename_buf; - } - else { - treename = DATA_("NodeTree"); - } - - if (!ntreeTypeFind(idname)) { - BKE_reportf(op->reports, RPT_ERROR, "Node tree type %s undefined", idname); - return OPERATOR_CANCELLED; - } - - ntree = ntreeAddTree(bmain, treename, idname); - - /* hook into UI */ - UI_context_active_but_prop_get_templateID(C, &ptr, &prop); - - if (prop) { - /* RNA_property_pointer_set increases the user count, - * fixed here as the editor is the initial user. - */ - id_us_min(&ntree->id); - - RNA_id_pointer_create(&ntree->id, &idptr); - RNA_property_pointer_set(&ptr, prop, idptr, NULL); - RNA_property_update(C, &ptr, prop); - } - else if (snode) { - snode->nodetree = ntree; - - ED_node_tree_update(C); - } - - return OPERATOR_FINISHED; -} - -static const EnumPropertyItem *new_node_tree_type_itemf(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), - bool *r_free) -{ - return rna_node_tree_type_itemf(NULL, NULL, r_free); -} - -void NODE_OT_new_node_tree(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "New Node Tree"; - ot->idname = "NODE_OT_new_node_tree"; - ot->description = "Create a new node tree"; - - /* api callbacks */ - ot->exec = new_node_tree_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - prop = RNA_def_enum(ot->srna, "type", DummyRNA_NULL_items, 0, "Tree Type", ""); - RNA_def_enum_funcs(prop, new_node_tree_type_itemf); - RNA_def_string(ot->srna, "name", "NodeTree", MAX_ID_NAME - 2, "Name", ""); -} - -/** \} */ diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc new file mode 100644 index 00000000000..7276688e986 --- /dev/null +++ b/source/blender/editors/space_node/node_add.cc @@ -0,0 +1,1019 @@ +/* + * 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. + */ + +/** \file + * \ingroup spnode + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_collection_types.h" +#include "DNA_node_types.h" +#include "DNA_texture_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_texture.h" + +#include "DEG_depsgraph_build.h" + +#include "ED_node.h" /* own include */ +#include "ED_render.h" +#include "ED_screen.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_view2d.h" + +#include "node_intern.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +/** + * XXX Does some additional initialization on top of #nodeAddNode + * Can be used with both custom and static nodes, + * if `idname == nullptr` the static int type will be used instead. + */ +bNode *node_add_node(const bContext *C, const char *idname, int type, float locx, float locy) +{ + SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main(C); + bNode *node = nullptr; + + node_deselect_all(snode); + + if (idname) { + node = nodeAddNode(C, snode->edittree, idname); + } + else { + node = nodeAddStaticNode(C, snode->edittree, type); + } + BLI_assert(node && node->typeinfo); + + /* Position mouse in node header. */ + node->locx = locx - NODE_DY * 1.5f / UI_DPI_FAC; + node->locy = locy + NODE_DY * 0.5f / UI_DPI_FAC; + + nodeSetSelected(node, true); + + ntreeUpdateTree(bmain, snode->edittree); + ED_node_set_active(bmain, snode->edittree, node, nullptr); + + snode_update(snode, node); + + if (snode->nodetree->type == NTREE_TEXTURE) { + ntreeTexCheckCyclics(snode->edittree); + } + + return node; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Reroute Operator + * \{ */ + +static bool add_reroute_intersect_check(bNodeLink *link, + float mcoords[][2], + int tot, + float result[2]) +{ + float coord_array[NODE_LINK_RESOL + 1][2]; + + if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) { + for (int i = 0; i < tot - 1; i++) { + for (int b = 0; b < NODE_LINK_RESOL; b++) { + if (isect_seg_seg_v2_point( + mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1], result) > 0) { + return true; + } + } + } + } + return false; +} + +struct bNodeSocketLink { + struct bNodeSocketLink *next, *prev; + + struct bNodeSocket *sock; + struct bNodeLink *link; + float point[2]; +}; + +static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb, + bNodeSocket *sock, + bNodeLink *link, + const float point[2]) +{ + bNodeSocketLink *socklink, *prev; + + socklink = (bNodeSocketLink *)MEM_callocN(sizeof(bNodeSocketLink), "socket link"); + socklink->sock = sock; + socklink->link = link; + copy_v2_v2(socklink->point, point); + + for (prev = (bNodeSocketLink *)lb->last; prev; prev = prev->prev) { + if (prev->sock == sock) { + break; + } + } + BLI_insertlinkafter(lb, prev, socklink); + return socklink; +} + +static bNodeSocketLink *add_reroute_do_socket_section(bContext *C, + bNodeSocketLink *socklink, + int in_out) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *reroute_node = nullptr; + bNodeSocket *cursock = socklink->sock; + float insert_point[2]; + int num_links; + + zero_v2(insert_point); + num_links = 0; + + while (socklink && socklink->sock == cursock) { + if (!(socklink->link->flag & NODE_LINK_TEST)) { + socklink->link->flag |= NODE_LINK_TEST; + + /* create the reroute node for this cursock */ + if (!reroute_node) { + reroute_node = nodeAddStaticNode(C, ntree, NODE_REROUTE); + + /* add a single link to/from the reroute node to replace multiple links */ + if (in_out == SOCK_OUT) { + nodeAddLink(ntree, + socklink->link->fromnode, + socklink->link->fromsock, + reroute_node, + (bNodeSocket *)reroute_node->inputs.first); + } + else { + nodeAddLink(ntree, + reroute_node, + (bNodeSocket *)reroute_node->outputs.first, + socklink->link->tonode, + socklink->link->tosock); + } + } + + /* insert the reroute node into the link */ + if (in_out == SOCK_OUT) { + socklink->link->fromnode = reroute_node; + socklink->link->fromsock = (bNodeSocket *)reroute_node->outputs.first; + } + else { + socklink->link->tonode = reroute_node; + socklink->link->tosock = (bNodeSocket *)reroute_node->inputs.first; + } + + add_v2_v2(insert_point, socklink->point); + num_links++; + } + socklink = socklink->next; + } + + if (num_links > 0) { + /* average cut point from shared links */ + mul_v2_fl(insert_point, 1.0f / num_links); + + reroute_node->locx = insert_point[0] / UI_DPI_FAC; + reroute_node->locy = insert_point[1] / UI_DPI_FAC; + } + + return socklink; +} + +static int add_reroute_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + bNodeTree *ntree = snode->edittree; + float mcoords[256][2]; + int i = 0; + + /* Get the cut path */ + RNA_BEGIN (op->ptr, itemptr, "path") { + float loc[2]; + + RNA_float_get_array(&itemptr, "loc", loc); + UI_view2d_region_to_view( + ®ion->v2d, (short)loc[0], (short)loc[1], &mcoords[i][0], &mcoords[i][1]); + i++; + if (i >= 256) { + break; + } + } + RNA_END; + + if (i > 1) { + ListBase output_links, input_links; + bNodeLink *link; + bNodeSocketLink *socklink; + float insert_point[2]; + + /* always first */ + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + node_deselect_all(snode); + + /* Find cut links and sort them by sockets */ + BLI_listbase_clear(&output_links); + BLI_listbase_clear(&input_links); + + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { + if (nodeLinkIsHidden(link)) { + continue; + } + if (add_reroute_intersect_check(link, mcoords, i, insert_point)) { + add_reroute_insert_socket_link(&output_links, link->fromsock, link, insert_point); + add_reroute_insert_socket_link(&input_links, link->tosock, link, insert_point); + + /* Clear flag */ + link->flag &= ~NODE_LINK_TEST; + } + } + + /* Create reroute nodes for intersected links. + * Only one reroute if links share the same input/output socket. + */ + socklink = (bNodeSocketLink *)output_links.first; + while (socklink) { + socklink = add_reroute_do_socket_section(C, socklink, SOCK_OUT); + } + socklink = (bNodeSocketLink *)input_links.first; + while (socklink) { + socklink = add_reroute_do_socket_section(C, socklink, SOCK_IN); + } + + BLI_freelistN(&output_links); + BLI_freelistN(&input_links); + + /* always last */ + ntreeUpdateTree(CTX_data_main(C), ntree); + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; +} + +void NODE_OT_add_reroute(wmOperatorType *ot) +{ + ot->name = "Add Reroute"; + ot->idname = "NODE_OT_add_reroute"; + ot->description = "Add a reroute node"; + + ot->invoke = WM_gesture_lines_invoke; + ot->modal = WM_gesture_lines_modal; + ot->exec = add_reroute_exec; + ot->cancel = WM_gesture_lines_cancel; + + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + /* internal */ + RNA_def_int(ot->srna, "cursor", WM_CURSOR_CROSS, 0, INT_MAX, "Cursor", "", 0, INT_MAX); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Node Group Operator + * \{ */ + +static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain, + wmOperator *op, + bNodeTree *ntree) +{ + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); + + bNodeTree *node_group = (bNodeTree *)BKE_libblock_find_name(bmain, ID_NT, name); + if (!node_group) { + return nullptr; + } + + const char *disabled_hint = nullptr; + if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group, &disabled_hint)) { + if (disabled_hint) { + BKE_reportf(op->reports, + RPT_ERROR, + "Can not add node group '%s' to '%s':\n %s", + node_group->id.name + 2, + ntree->id.name + 2, + disabled_hint); + } + else { + BKE_reportf(op->reports, + RPT_ERROR, + "Can not add node group '%s' to '%s'", + node_group->id.name + 2, + ntree->id.name + 2); + } + + return nullptr; + } + + return node_group; +} + +static int node_add_group_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNodeTree *node_group; + + if (!(node_group = node_add_group_get_and_poll_group_node_tree(bmain, op, ntree))) { + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + bNode *group_node = node_add_node(C, + node_group_idname(C), + (node_group->type == NTREE_CUSTOM) ? NODE_CUSTOM_GROUP : + NODE_GROUP, + snode->runtime->cursor[0], + snode->runtime->cursor[1]); + if (!group_node) { + BKE_report(op->reports, RPT_WARNING, "Could not add node group"); + return OPERATOR_CANCELLED; + } + + group_node->id = &node_group->id; + id_us_plus(group_node->id); + + nodeSetActive(ntree, group_node); + ntreeUpdateTree(bmain, node_group); + ntreeUpdateTree(bmain, ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +static int node_add_group_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + SpaceNode *snode = CTX_wm_space_node(C); + + /* Convert mouse coordinates to v2d space. */ + UI_view2d_region_to_view(®ion->v2d, + event->mval[0], + event->mval[1], + &snode->runtime->cursor[0], + &snode->runtime->cursor[1]); + + snode->runtime->cursor[0] /= UI_DPI_FAC; + snode->runtime->cursor[1] /= UI_DPI_FAC; + + return node_add_group_exec(C, op); +} + +void NODE_OT_add_group(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Node Group"; + ot->description = "Add an existing node group to the current node editor"; + ot->idname = "NODE_OT_add_group"; + + /* callbacks */ + ot->exec = node_add_group_exec; + ot->invoke = node_add_group_invoke; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Node Object Operator + * \{ */ + +static Object *node_add_object_get_and_poll_object_node_tree(Main *bmain, wmOperator *op) +{ + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); + + Object *object = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); + if (!object) { + return nullptr; + } + + return object; +} + +static int node_add_object_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + Object *object; + + if (!(object = node_add_object_get_and_poll_object_node_tree(bmain, op))) { + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + bNode *object_node = node_add_node( + C, nullptr, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]); + if (!object_node) { + BKE_report(op->reports, RPT_WARNING, "Could not add node object"); + return OPERATOR_CANCELLED; + } + + bNodeSocket *sock = nodeFindSocket(object_node, SOCK_IN, "Object"); + if (!sock) { + BKE_report(op->reports, RPT_WARNING, "Could not find node object socket"); + return OPERATOR_CANCELLED; + } + + bNodeSocketValueObject *socket_data = (bNodeSocketValueObject *)sock->default_value; + socket_data->value = object; + id_us_plus(&object->id); + + nodeSetActive(ntree, object_node); + ntreeUpdateTree(bmain, ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + ED_node_tag_update_nodetree(bmain, ntree, object_node); + DEG_relations_tag_update(bmain); + + return OPERATOR_FINISHED; +} + +static int node_add_object_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + SpaceNode *snode = CTX_wm_space_node(C); + + /* Convert mouse coordinates to v2d space. */ + UI_view2d_region_to_view(®ion->v2d, + event->mval[0], + event->mval[1], + &snode->runtime->cursor[0], + &snode->runtime->cursor[1]); + + snode->runtime->cursor[0] /= UI_DPI_FAC; + snode->runtime->cursor[1] /= UI_DPI_FAC; + + return node_add_object_exec(C, op); +} + +static bool node_add_object_poll(bContext *C) +{ + const SpaceNode *snode = CTX_wm_space_node(C); + return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) && + !UI_but_active_drop_name(C); +} + +void NODE_OT_add_object(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Node Object"; + ot->description = "Add an object info node to the current node editor"; + ot->idname = "NODE_OT_add_object"; + + /* callbacks */ + ot->exec = node_add_object_exec; + ot->invoke = node_add_object_invoke; + ot->poll = node_add_object_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string(ot->srna, "name", "Object", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Node Texture Operator + * \{ */ + +static Tex *node_add_texture_get_and_poll_texture_node_tree(Main *bmain, wmOperator *op) +{ + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); + + Tex *texture = (Tex *)BKE_libblock_find_name(bmain, ID_TE, name); + if (!texture) { + return nullptr; + } + + return texture; +} + +static int node_add_texture_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + Tex *texture; + + if (!(texture = node_add_texture_get_and_poll_texture_node_tree(bmain, op))) { + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + bNode *texture_node = node_add_node(C, + nullptr, + GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, + snode->runtime->cursor[0], + snode->runtime->cursor[1]); + if (!texture_node) { + BKE_report(op->reports, RPT_WARNING, "Could not add texture node"); + return OPERATOR_CANCELLED; + } + + texture_node->id = &texture->id; + id_us_plus(&texture->id); + + nodeSetActive(ntree, texture_node); + ntreeUpdateTree(bmain, ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + ED_node_tag_update_nodetree(bmain, ntree, texture_node); + + return OPERATOR_FINISHED; +} + +static int node_add_texture_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + SpaceNode *snode = CTX_wm_space_node(C); + + /* Convert mouse coordinates to v2d space. */ + UI_view2d_region_to_view(®ion->v2d, + event->mval[0], + event->mval[1], + &snode->runtime->cursor[0], + &snode->runtime->cursor[1]); + + snode->runtime->cursor[0] /= UI_DPI_FAC; + snode->runtime->cursor[1] /= UI_DPI_FAC; + + return node_add_texture_exec(C, op); +} + +static bool node_add_texture_poll(bContext *C) +{ + const SpaceNode *snode = CTX_wm_space_node(C); + return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) && + !UI_but_active_drop_name(C); +} + +void NODE_OT_add_texture(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Node Texture"; + ot->description = "Add a texture to the current node editor"; + ot->idname = "NODE_OT_add_texture"; + + /* callbacks */ + ot->exec = node_add_texture_exec; + ot->invoke = node_add_texture_invoke; + ot->poll = node_add_texture_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string( + ot->srna, "name", "Texture", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Node Collection Operator + * \{ */ + +static Collection *node_add_collection_get_and_poll_collection_node_tree(Main *bmain, + wmOperator *op) +{ + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); + + Collection *collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); + if (!collection) { + return nullptr; + } + + return collection; +} + +static int node_add_collection_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + Collection *collection; + + if (!(collection = node_add_collection_get_and_poll_collection_node_tree(bmain, op))) { + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + bNode *collection_node = node_add_node( + C, nullptr, GEO_NODE_COLLECTION_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]); + if (!collection_node) { + BKE_report(op->reports, RPT_WARNING, "Could not add node collection"); + return OPERATOR_CANCELLED; + } + + bNodeSocket *sock = nodeFindSocket(collection_node, SOCK_IN, "Collection"); + if (!sock) { + BKE_report(op->reports, RPT_WARNING, "Could not find node collection socket"); + return OPERATOR_CANCELLED; + } + + bNodeSocketValueCollection *socket_data = (bNodeSocketValueCollection *)sock->default_value; + socket_data->value = collection; + id_us_plus(&collection->id); + + nodeSetActive(ntree, collection_node); + ntreeUpdateTree(bmain, ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + ED_node_tag_update_nodetree(bmain, ntree, collection_node); + + return OPERATOR_FINISHED; +} + +static int node_add_collection_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + SpaceNode *snode = CTX_wm_space_node(C); + + /* Convert mouse coordinates to v2d space. */ + UI_view2d_region_to_view(®ion->v2d, + event->mval[0], + event->mval[1], + &snode->runtime->cursor[0], + &snode->runtime->cursor[1]); + + snode->runtime->cursor[0] /= UI_DPI_FAC; + snode->runtime->cursor[1] /= UI_DPI_FAC; + + return node_add_collection_exec(C, op); +} + +static bool node_add_collection_poll(bContext *C) +{ + const SpaceNode *snode = CTX_wm_space_node(C); + return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) && + !UI_but_active_drop_name(C); +} + +void NODE_OT_add_collection(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Node Collection"; + ot->description = "Add an collection info node to the current node editor"; + ot->idname = "NODE_OT_add_collection"; + + /* callbacks */ + ot->exec = node_add_collection_exec; + ot->invoke = node_add_collection_invoke; + ot->poll = node_add_collection_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string( + ot->srna, "name", "Collection", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add File Node Operator + * \{ */ + +static bool node_add_file_poll(bContext *C) +{ + const SpaceNode *snode = CTX_wm_space_node(C); + return ED_operator_node_editable(C) && + ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT); +} + +static int node_add_file_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node; + Image *ima; + int type = 0; + + ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM); + if (!ima) { + return OPERATOR_CANCELLED; + } + + switch (snode->nodetree->type) { + case NTREE_SHADER: + type = SH_NODE_TEX_IMAGE; + break; + case NTREE_TEXTURE: + type = TEX_NODE_IMAGE; + break; + case NTREE_COMPOSIT: + type = CMP_NODE_IMAGE; + break; + default: + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + node = node_add_node(C, nullptr, type, snode->runtime->cursor[0], snode->runtime->cursor[1]); + + if (!node) { + BKE_report(op->reports, RPT_WARNING, "Could not add an image node"); + return OPERATOR_CANCELLED; + } + + node->id = (ID *)ima; + + /* When adding new image file via drag-drop we need to load imbuf in order + * to get proper image source. + */ + if (RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_image_signal(bmain, ima, nullptr, IMA_SIGNAL_RELOAD); + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); + } + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +static int node_add_file_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + SpaceNode *snode = CTX_wm_space_node(C); + + /* convert mouse coordinates to v2d space */ + UI_view2d_region_to_view(®ion->v2d, + event->mval[0], + event->mval[1], + &snode->runtime->cursor[0], + &snode->runtime->cursor[1]); + + snode->runtime->cursor[0] /= UI_DPI_FAC; + snode->runtime->cursor[1] /= UI_DPI_FAC; + + if (RNA_struct_property_is_set(op->ptr, "filepath") || + RNA_struct_property_is_set(op->ptr, "name")) { + return node_add_file_exec(C, op); + } + return WM_operator_filesel(C, op, event); +} + +void NODE_OT_add_file(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add File Node"; + ot->description = "Add a file node to the current node editor"; + ot->idname = "NODE_OT_add_file"; + + /* callbacks */ + ot->exec = node_add_file_exec; + ot->invoke = node_add_file_invoke; + ot->poll = node_add_file_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, + FILE_SPECIAL, + FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, + FILE_DEFAULTDISPLAY, + FILE_SORT_DEFAULT); + RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Mask Node Operator + * \{ */ + +static bool node_add_mask_poll(bContext *C) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + return ED_operator_node_editable(C) && snode->nodetree->type == NTREE_COMPOSIT; +} + +static int node_add_mask_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node; + ID *mask = nullptr; + + /* check input variables */ + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); + mask = BKE_libblock_find_name(bmain, ID_MSK, name); + if (!mask) { + BKE_reportf(op->reports, RPT_ERROR, "Mask '%s' not found", name); + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + node = node_add_node( + C, nullptr, CMP_NODE_MASK, snode->runtime->cursor[0], snode->runtime->cursor[1]); + + if (!node) { + BKE_report(op->reports, RPT_WARNING, "Could not add a mask node"); + return OPERATOR_CANCELLED; + } + + node->id = mask; + id_us_plus(mask); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_add_mask(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Mask Node"; + ot->description = "Add a mask node to the current node editor"; + ot->idname = "NODE_OT_add_mask"; + + /* callbacks */ + ot->exec = node_add_mask_exec; + ot->poll = node_add_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name New Node Tree Operator + * \{ */ + +static int new_node_tree_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + Main *bmain = CTX_data_main(C); + bNodeTree *ntree; + PointerRNA ptr, idptr; + PropertyRNA *prop; + const char *idname; + char treename_buf[MAX_ID_NAME - 2]; + const char *treename; + + if (RNA_struct_property_is_set(op->ptr, "type")) { + prop = RNA_struct_find_property(op->ptr, "type"); + RNA_property_enum_identifier(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &idname); + } + else if (snode) { + idname = snode->tree_idname; + } + else { + return OPERATOR_CANCELLED; + } + + if (RNA_struct_property_is_set(op->ptr, "name")) { + RNA_string_get(op->ptr, "name", treename_buf); + treename = treename_buf; + } + else { + treename = DATA_("NodeTree"); + } + + if (!ntreeTypeFind(idname)) { + BKE_reportf(op->reports, RPT_ERROR, "Node tree type %s undefined", idname); + return OPERATOR_CANCELLED; + } + + ntree = ntreeAddTree(bmain, treename, idname); + + /* hook into UI */ + UI_context_active_but_prop_get_templateID(C, &ptr, &prop); + + if (prop) { + /* RNA_property_pointer_set increases the user count, + * fixed here as the editor is the initial user. + */ + id_us_min(&ntree->id); + + RNA_id_pointer_create(&ntree->id, &idptr); + RNA_property_pointer_set(&ptr, prop, idptr, nullptr); + RNA_property_update(C, &ptr, prop); + } + else if (snode) { + snode->nodetree = ntree; + + ED_node_tree_update(C); + } + + return OPERATOR_FINISHED; +} + +static const EnumPropertyItem *new_node_tree_type_itemf(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + return rna_node_tree_type_itemf(nullptr, nullptr, r_free); +} + +void NODE_OT_new_node_tree(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "New Node Tree"; + ot->idname = "NODE_OT_new_node_tree"; + ot->description = "Create a new node tree"; + + /* api callbacks */ + ot->exec = new_node_tree_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + prop = RNA_def_enum(ot->srna, "type", DummyRNA_NULL_items, 0, "Tree Type", ""); + RNA_def_enum_funcs(prop, new_node_tree_type_itemf); + RNA_def_string(ot->srna, "name", "NodeTree", MAX_ID_NAME - 2, "Name", ""); +} + +/** \} */ diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c deleted file mode 100644 index 50fa8b28468..00000000000 --- a/source/blender/editors/space_node/node_edit.c +++ /dev/null @@ -1,2834 +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. - */ - -/** \file - * \ingroup spnode - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_light_types.h" -#include "DNA_material_types.h" -#include "DNA_node_types.h" -#include "DNA_text_types.h" -#include "DNA_world_types.h" - -#include "BLI_blenlib.h" -#include "BLI_math.h" - -#include "BKE_context.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_node.h" -#include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_workspace.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" -#include "DEG_depsgraph_query.h" - -#include "RE_engine.h" -#include "RE_pipeline.h" - -#include "ED_node.h" /* own include */ -#include "ED_render.h" -#include "ED_screen.h" -#include "ED_select_utils.h" - -#include "RNA_access.h" -#include "RNA_define.h" -#include "RNA_enum_types.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "UI_view2d.h" - -#include "GPU_material.h" - -#include "IMB_imbuf_types.h" - -#include "NOD_composite.h" -#include "NOD_geometry.h" -#include "NOD_shader.h" -#include "NOD_texture.h" -#include "node_intern.h" /* own include */ - -#define USE_ESC_COMPO - -/* ***************** composite job manager ********************** */ - -enum { - COM_RECALC_COMPOSITE = 1, - COM_RECALC_VIEWER = 2, -}; - -typedef struct CompoJob { - /* Input parameters. */ - Main *bmain; - Scene *scene; - ViewLayer *view_layer; - bNodeTree *ntree; - int recalc_flags; - /* Evaluated state/ */ - Depsgraph *compositor_depsgraph; - bNodeTree *localtree; - /* Jon system integration. */ - const short *stop; - short *do_update; - float *progress; -} CompoJob; - -float node_socket_calculate_height(const bNodeSocket *socket) -{ - float sock_height = NODE_SOCKSIZE * 2.0f; - if (socket->flag & SOCK_MULTI_INPUT) { - sock_height += max_ii(NODE_MULTI_INPUT_LINK_GAP * 0.5f * socket->total_inputs, NODE_SOCKSIZE); - } - return sock_height; -} - -void node_link_calculate_multi_input_position(const float socket_x, - const float socket_y, - const int index, - const int total_inputs, - float r[2]) -{ - float offset = (total_inputs * NODE_MULTI_INPUT_LINK_GAP - NODE_MULTI_INPUT_LINK_GAP) * 0.5; - r[0] = socket_x - NODE_SOCKSIZE * 0.5f; - r[1] = socket_y - offset + (index * NODE_MULTI_INPUT_LINK_GAP); -} - -static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags) -{ - LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) { - if (node->type == CMP_NODE_COMPOSITE) { - if (recalc_flags & COM_RECALC_COMPOSITE) { - node->flag |= NODE_DO_OUTPUT_RECALC; - } - } - else if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { - if (recalc_flags & COM_RECALC_VIEWER) { - node->flag |= NODE_DO_OUTPUT_RECALC; - } - } - else if (node->type == NODE_GROUP) { - if (node->id) { - compo_tag_output_nodes((bNodeTree *)node->id, recalc_flags); - } - } - } -} - -static int compo_get_recalc_flags(const bContext *C) -{ - wmWindowManager *wm = CTX_wm_manager(C); - int recalc_flags = 0; - - LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - const bScreen *screen = WM_window_get_active_screen(win); - - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (area->spacetype == SPACE_IMAGE) { - SpaceImage *sima = area->spacedata.first; - if (sima->image) { - if (sima->image->type == IMA_TYPE_R_RESULT) { - recalc_flags |= COM_RECALC_COMPOSITE; - } - else if (sima->image->type == IMA_TYPE_COMPOSITE) { - recalc_flags |= COM_RECALC_VIEWER; - } - } - } - else if (area->spacetype == SPACE_NODE) { - SpaceNode *snode = area->spacedata.first; - if (snode->flag & SNODE_BACKDRAW) { - recalc_flags |= COM_RECALC_VIEWER; - } - } - } - } - - return recalc_flags; -} - -/* called by compo, only to check job 'stop' value */ -static int compo_breakjob(void *cjv) -{ - CompoJob *cj = cjv; - - /* without G.is_break 'ESC' wont quit - which annoys users */ - return (*(cj->stop) -#ifdef USE_ESC_COMPO - || G.is_break -#endif - ); -} - -/* called by compo, wmJob sends notifier */ -static void compo_statsdrawjob(void *cjv, const char *UNUSED(str)) -{ - CompoJob *cj = cjv; - - *(cj->do_update) = true; -} - -/* called by compo, wmJob sends notifier */ -static void compo_redrawjob(void *cjv) -{ - CompoJob *cj = cjv; - - *(cj->do_update) = true; -} - -static void compo_freejob(void *cjv) -{ - CompoJob *cj = cjv; - - if (cj->localtree) { - ntreeLocalMerge(cj->bmain, cj->localtree, cj->ntree); - } - if (cj->compositor_depsgraph != NULL) { - DEG_graph_free(cj->compositor_depsgraph); - } - MEM_freeN(cj); -} - -/* only now we copy the nodetree, so adding many jobs while - * sliding buttons doesn't frustrate */ -static void compo_initjob(void *cjv) -{ - CompoJob *cj = cjv; - Main *bmain = cj->bmain; - Scene *scene = cj->scene; - ViewLayer *view_layer = cj->view_layer; - - cj->compositor_depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); - DEG_graph_build_for_compositor_preview(cj->compositor_depsgraph, cj->ntree); - - /* NOTE: Don't update animation to preserve unkeyed changes, this means can not use - * evaluate_on_framechange. */ - DEG_evaluate_on_refresh(cj->compositor_depsgraph); - - bNodeTree *ntree_eval = (bNodeTree *)DEG_get_evaluated_id(cj->compositor_depsgraph, - &cj->ntree->id); - - cj->localtree = ntreeLocalize(ntree_eval); - - if (cj->recalc_flags) { - compo_tag_output_nodes(cj->localtree, cj->recalc_flags); - } -} - -/* called before redraw notifiers, it moves finished previews over */ -static void compo_updatejob(void *UNUSED(cjv)) -{ - WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, NULL); -} - -static void compo_progressjob(void *cjv, float progress) -{ - CompoJob *cj = cjv; - - *(cj->progress) = progress; -} - -/* only this runs inside thread */ -static void compo_startjob(void *cjv, - /* Cannot be const, this function implements wm_jobs_start_callback. - * NOLINTNEXTLINE: readability-non-const-parameter. */ - short *stop, - short *do_update, - float *progress) -{ - CompoJob *cj = cjv; - bNodeTree *ntree = cj->localtree; - Scene *scene = cj->scene; - - if (scene->use_nodes == false) { - return; - } - - cj->stop = stop; - cj->do_update = do_update; - cj->progress = progress; - - ntree->test_break = compo_breakjob; - ntree->tbh = cj; - ntree->stats_draw = compo_statsdrawjob; - ntree->sdh = cj; - ntree->progress = compo_progressjob; - ntree->prh = cj; - ntree->update_draw = compo_redrawjob; - ntree->udh = cj; - - // XXX BIF_store_spare(); - /* 1 is do_previews */ - - if ((cj->scene->r.scemode & R_MULTIVIEW) == 0) { - ntreeCompositExecTree(cj->scene, - ntree, - &cj->scene->r, - false, - true, - &scene->view_settings, - &scene->display_settings, - ""); - } - else { - LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) { - if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) { - continue; - } - ntreeCompositExecTree(cj->scene, - ntree, - &cj->scene->r, - false, - true, - &scene->view_settings, - &scene->display_settings, - srv->name); - } - } - - ntree->test_break = NULL; - ntree->stats_draw = NULL; - ntree->progress = NULL; -} - -/** - * \param scene_owner: is the owner of the job, - * we don't use it for anything else currently so could also be a void pointer, - * but for now keep it an 'Scene' for consistency. - * - * \note only call from spaces `refresh` callbacks, not direct! - use with care. - */ -void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - - /* to fix bug: T32272. */ - if (G.is_rendering) { - return; - } - -#ifdef USE_ESC_COMPO - G.is_break = false; -#endif - - BKE_image_backup_render( - scene, BKE_image_ensure_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"), false); - - wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - scene_owner, - "Compositing", - WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS, - WM_JOB_TYPE_COMPOSITE); - CompoJob *cj = MEM_callocN(sizeof(CompoJob), "compo job"); - - /* customdata for preview thread */ - cj->bmain = bmain; - cj->scene = scene; - cj->view_layer = view_layer; - cj->ntree = nodetree; - cj->recalc_flags = compo_get_recalc_flags(C); - - /* setup job */ - WM_jobs_customdata_set(wm_job, cj, compo_freejob); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_COMPO_RESULT, NC_SCENE | ND_COMPO_RESULT); - WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, NULL); - - WM_jobs_start(CTX_wm_manager(C), wm_job); -} - -/* ***************************************** */ - -/* operator poll callback */ -bool composite_node_active(bContext *C) -{ - if (ED_operator_node_active(C)) { - SpaceNode *snode = CTX_wm_space_node(C); - if (ED_node_is_compositor(snode)) { - return true; - } - } - return false; -} - -/* operator poll callback */ -bool composite_node_editable(bContext *C) -{ - if (ED_operator_node_editable(C)) { - SpaceNode *snode = CTX_wm_space_node(C); - if (ED_node_is_compositor(snode)) { - return true; - } - } - return false; -} - -void snode_dag_update(bContext *C, SpaceNode *snode) -{ - Main *bmain = CTX_data_main(C); - - /* for groups, update all ID's using this */ - if (snode->edittree != snode->nodetree) { - FOREACH_NODETREE_BEGIN (bmain, tntree, id) { - if (ntreeHasTree(tntree, snode->edittree)) { - DEG_id_tag_update(id, 0); - } - } - FOREACH_NODETREE_END; - } - - DEG_id_tag_update(snode->id, 0); - DEG_id_tag_update(&snode->nodetree->id, 0); -} - -void snode_notify(bContext *C, SpaceNode *snode) -{ - ID *id = snode->id; - - WM_event_add_notifier(C, NC_NODE | NA_EDITED, NULL); - - if (ED_node_is_shader(snode)) { - if (GS(id->name) == ID_MA) { - WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id); - } - else if (GS(id->name) == ID_LA) { - WM_main_add_notifier(NC_LAMP | ND_LIGHTING, id); - } - else if (GS(id->name) == ID_WO) { - WM_main_add_notifier(NC_WORLD | ND_WORLD, id); - } - } - else if (ED_node_is_compositor(snode)) { - WM_event_add_notifier(C, NC_SCENE | ND_NODES, id); - } - else if (ED_node_is_texture(snode)) { - WM_event_add_notifier(C, NC_TEXTURE | ND_NODES, id); - } - else if (ED_node_is_geometry(snode)) { - WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id); - } -} - -void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo) -{ - if (typeinfo) { - BLI_strncpy(snode->tree_idname, typeinfo->idname, sizeof(snode->tree_idname)); - } - else { - snode->tree_idname[0] = '\0'; - } -} - -bool ED_node_is_compositor(struct SpaceNode *snode) -{ - return STREQ(snode->tree_idname, ntreeType_Composite->idname); -} - -bool ED_node_is_shader(struct SpaceNode *snode) -{ - return STREQ(snode->tree_idname, ntreeType_Shader->idname); -} - -bool ED_node_is_texture(struct SpaceNode *snode) -{ - return STREQ(snode->tree_idname, ntreeType_Texture->idname); -} - -bool ED_node_is_geometry(struct SpaceNode *snode) -{ - return STREQ(snode->tree_idname, ntreeType_Geometry->idname); -} - -/* assumes nothing being done in ntree yet, sets the default in/out node */ -/* called from shading buttons or header */ -void ED_node_shader_default(const bContext *C, ID *id) -{ - Main *bmain = CTX_data_main(C); - - if (GS(id->name) == ID_MA) { - /* Materials */ - Object *ob = CTX_data_active_object(C); - Material *ma = (Material *)id; - Material *ma_default; - - if (ob && ob->type == OB_VOLUME) { - ma_default = BKE_material_default_volume(); - } - else { - ma_default = BKE_material_default_surface(); - } - - ma->nodetree = ntreeCopyTree(bmain, ma_default->nodetree); - ntreeUpdateTree(bmain, ma->nodetree); - } - else if (ELEM(GS(id->name), ID_WO, ID_LA)) { - /* Emission */ - bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); - bNode *shader, *output; - - if (GS(id->name) == ID_WO) { - World *world = (World *)id; - world->nodetree = ntree; - - shader = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND); - output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD); - nodeAddLink(ntree, - shader, - nodeFindSocket(shader, SOCK_OUT, "Background"), - output, - nodeFindSocket(output, SOCK_IN, "Surface")); - - bNodeSocket *color_sock = nodeFindSocket(shader, SOCK_IN, "Color"); - copy_v3_v3(((bNodeSocketValueRGBA *)color_sock->default_value)->value, &world->horr); - } - else { - Light *light = (Light *)id; - light->nodetree = ntree; - - shader = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION); - output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_LIGHT); - nodeAddLink(ntree, - shader, - nodeFindSocket(shader, SOCK_OUT, "Emission"), - output, - nodeFindSocket(output, SOCK_IN, "Surface")); - } - - shader->locx = 10.0f; - shader->locy = 300.0f; - output->locx = 300.0f; - output->locy = 300.0f; - nodeSetActive(ntree, output); - ntreeUpdateTree(bmain, ntree); - } - else { - printf("ED_node_shader_default called on wrong ID type.\n"); - return; - } -} - -/* assumes nothing being done in ntree yet, sets the default in/out node */ -/* called from shading buttons or header */ -void ED_node_composit_default(const bContext *C, struct Scene *sce) -{ - /* but lets check it anyway */ - if (sce->nodetree) { - if (G.debug & G_DEBUG) { - printf("error in composite initialize\n"); - } - return; - } - - sce->nodetree = ntreeAddTree(NULL, "Compositing Nodetree", ntreeType_Composite->idname); - - sce->nodetree->chunksize = 256; - sce->nodetree->edit_quality = NTREE_QUALITY_HIGH; - sce->nodetree->render_quality = NTREE_QUALITY_HIGH; - - bNode *out = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_COMPOSITE); - out->locx = 300.0f; - out->locy = 400.0f; - - bNode *in = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_R_LAYERS); - in->locx = 10.0f; - in->locy = 400.0f; - nodeSetActive(sce->nodetree, in); - - /* links from color to color */ - bNodeSocket *fromsock = in->outputs.first; - bNodeSocket *tosock = out->inputs.first; - nodeAddLink(sce->nodetree, in, fromsock, out, tosock); - - ntreeUpdateTree(CTX_data_main(C), sce->nodetree); -} - -/* assumes nothing being done in ntree yet, sets the default in/out node */ -/* called from shading buttons or header */ -void ED_node_texture_default(const bContext *C, Tex *tex) -{ - /* but lets check it anyway */ - if (tex->nodetree) { - if (G.debug & G_DEBUG) { - printf("error in texture initialize\n"); - } - return; - } - - tex->nodetree = ntreeAddTree(NULL, "Texture Nodetree", ntreeType_Texture->idname); - - bNode *out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT); - out->locx = 300.0f; - out->locy = 300.0f; - - bNode *in = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_CHECKER); - in->locx = 10.0f; - in->locy = 300.0f; - nodeSetActive(tex->nodetree, in); - - bNodeSocket *fromsock = in->outputs.first; - bNodeSocket *tosock = out->inputs.first; - nodeAddLink(tex->nodetree, in, fromsock, out, tosock); - - ntreeUpdateTree(CTX_data_main(C), tex->nodetree); -} - -/* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */ -void snode_set_context(const bContext *C) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTreeType *treetype = ntreeTypeFind(snode->tree_idname); - bNodeTree *ntree = snode->nodetree; - ID *id = snode->id, *from = snode->from; - - /* check the tree type */ - if (!treetype || (treetype->poll && !treetype->poll(C, treetype))) { - /* invalid tree type, skip - * NB: not resetting the node path here, invalid bNodeTreeType - * may still be registered at a later point. - */ - return; - } - - if (snode->nodetree && !STREQ(snode->nodetree->idname, snode->tree_idname)) { - /* current tree does not match selected type, clear tree path */ - ntree = NULL; - id = NULL; - from = NULL; - } - - if (!(snode->flag & SNODE_PIN) || ntree == NULL) { - if (treetype->get_from_context) { - /* reset and update from context */ - ntree = NULL; - id = NULL; - from = NULL; - - treetype->get_from_context(C, treetype, &ntree, &id, &from); - } - } - - if (snode->nodetree != ntree || snode->id != id || snode->from != from || - (snode->treepath.last == NULL && ntree)) { - ED_node_tree_start(snode, ntree, id, from); - } -} - -void snode_update(SpaceNode *snode, bNode *node) -{ - /* XXX this only updates nodes in the current node space tree path. - * The function supposedly should update any potential group node linking to changed tree, - * this really requires a working depsgraph ... - */ - - /* update all edited group nodes */ - bNodeTreePath *path = snode->treepath.last; - if (path) { - bNodeTree *ngroup = path->nodetree; - for (path = path->prev; path; path = path->prev) { - nodeUpdateID(path->nodetree, (ID *)ngroup); - ngroup = path->nodetree; - } - } - - if (node) { - nodeUpdate(snode->edittree, node); - } -} - -void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed) -{ - const bool was_active_texture = (node->flag & NODE_ACTIVE_TEXTURE) != 0; - if (r_active_texture_changed) { - *r_active_texture_changed = false; - } - - nodeSetActive(ntree, node); - - if (node->type != NODE_GROUP) { - const bool was_output = (node->flag & NODE_DO_OUTPUT) != 0; - bool do_update = false; - - /* generic node group output: set node as active output */ - if (node->type == NODE_GROUP_OUTPUT) { - LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { - if (node_iter->type == NODE_GROUP_OUTPUT) { - node_iter->flag &= ~NODE_DO_OUTPUT; - } - } - - node->flag |= NODE_DO_OUTPUT; - if (!was_output) { - do_update = 1; - } - } - - /* tree specific activate calls */ - if (ntree->type == NTREE_SHADER) { - /* when we select a material, active texture is cleared, for buttons */ - if (node->id && ELEM(GS(node->id->name), ID_MA, ID_LA, ID_WO)) { - nodeClearActiveID(ntree, ID_TE); - } - - if (ELEM(node->type, - SH_NODE_OUTPUT_MATERIAL, - SH_NODE_OUTPUT_WORLD, - SH_NODE_OUTPUT_LIGHT, - SH_NODE_OUTPUT_LINESTYLE)) { - LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { - if (node_iter->type == node->type) { - node_iter->flag &= ~NODE_DO_OUTPUT; - } - } - - node->flag |= NODE_DO_OUTPUT; - if (was_output == 0) { - ED_node_tag_update_nodetree(bmain, ntree, node); - } - } - else if (do_update) { - ED_node_tag_update_nodetree(bmain, ntree, node); - } - - /* if active texture changed, free glsl materials */ - if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) { - LISTBASE_FOREACH (Material *, ma, &bmain->materials) { - if (ma->nodetree && ma->use_nodes && ntreeHasTree(ma->nodetree, ntree)) { - GPU_material_free(&ma->gpumaterial); - } - } - - LISTBASE_FOREACH (World *, wo, &bmain->worlds) { - if (wo->nodetree && wo->use_nodes && ntreeHasTree(wo->nodetree, ntree)) { - GPU_material_free(&wo->gpumaterial); - } - } - - if (r_active_texture_changed) { - *r_active_texture_changed = true; - } - ED_node_tag_update_nodetree(bmain, ntree, node); - WM_main_add_notifier(NC_IMAGE, NULL); - } - - WM_main_add_notifier(NC_MATERIAL | ND_NODES, node->id); - } - else if (ntree->type == NTREE_COMPOSIT) { - /* make active viewer, currently only 1 supported... */ - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { - LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { - if (ELEM(node_iter->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { - node_iter->flag &= ~NODE_DO_OUTPUT; - } - } - - node->flag |= NODE_DO_OUTPUT; - if (was_output == 0) { - ED_node_tag_update_nodetree(bmain, ntree, node); - } - - /* addnode() doesn't link this yet... */ - node->id = (ID *)BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - } - else if (node->type == CMP_NODE_COMPOSITE) { - if (was_output == 0) { - LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { - if (node_iter->type == CMP_NODE_COMPOSITE) { - node_iter->flag &= ~NODE_DO_OUTPUT; - } - } - - node->flag |= NODE_DO_OUTPUT; - ED_node_tag_update_nodetree(bmain, ntree, node); - } - } - else if (do_update) { - ED_node_tag_update_nodetree(bmain, ntree, node); - } - } - else if (ntree->type == NTREE_TEXTURE) { - /* XXX */ -#if 0 - if (node->id) { - BIF_preview_changed(-1); - allqueue(REDRAWBUTSSHADING, 1); - allqueue(REDRAWIPO, 0); - } -#endif - } - } -} - -void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) -{ - /* XXX This does not work due to layout functions relying on node->block, - * which only exists during actual drawing. Can we rely on valid totr rects? - */ - /* make sure nodes have correct bounding boxes after transform */ - // node_update_nodetree(C, ntree, 0.0f, 0.0f); -} - -/* ***************** generic operator functions for nodes ***************** */ - -#if 0 /* UNUSED */ - -static bool edit_node_poll(bContext *C) -{ - return ED_operator_node_active(C); -} - -static void edit_node_properties(wmOperatorType *ot) -{ - /* XXX could node be a context pointer? */ - RNA_def_string(ot->srna, "node", NULL, MAX_NAME, "Node", ""); - RNA_def_int(ot->srna, "socket", 0, 0, MAX_SOCKET, "Socket", "", 0, MAX_SOCKET); - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Side", ""); -} - -static int edit_node_invoke_properties(bContext *C, wmOperator *op) -{ - if (!RNA_struct_property_is_set(op->ptr, "node")) { - bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_Node).data; - if (!node) { - return 0; - } - else { - RNA_string_set(op->ptr, "node", node->name); - } - } - - if (!RNA_struct_property_is_set(op->ptr, "in_out")) { - RNA_enum_set(op->ptr, "in_out", SOCK_IN); - } - - if (!RNA_struct_property_is_set(op->ptr, "socket")) { - RNA_int_set(op->ptr, "socket", 0); - } - - return 1; -} - -static void edit_node_properties_get( - wmOperator *op, bNodeTree *ntree, bNode **r_node, bNodeSocket **r_sock, int *r_in_out) -{ - bNode *node; - bNodeSocket *sock = NULL; - char nodename[MAX_NAME]; - int sockindex; - int in_out; - - RNA_string_get(op->ptr, "node", nodename); - node = nodeFindNodebyName(ntree, nodename); - - in_out = RNA_enum_get(op->ptr, "in_out"); - - sockindex = RNA_int_get(op->ptr, "socket"); - switch (in_out) { - case SOCK_IN: - sock = BLI_findlink(&node->inputs, sockindex); - break; - case SOCK_OUT: - sock = BLI_findlink(&node->outputs, sockindex); - break; - } - - if (r_node) { - *r_node = node; - } - if (r_sock) { - *r_sock = sock; - } - if (r_in_out) { - *r_in_out = in_out; - } -} -#endif - -/* ************************** Node generic ************** */ - -/* is rct in visible part of node? */ -static bNode *visible_node(SpaceNode *snode, const rctf *rct) -{ - LISTBASE_FOREACH_BACKWARD (bNode *, node, &snode->edittree->nodes) { - if (BLI_rctf_isect(&node->totr, rct, NULL)) { - return node; - } - } - return NULL; -} - -/* ********************** size widget operator ******************** */ - -typedef struct NodeSizeWidget { - float mxstart, mystart; - float oldlocx, oldlocy; - float oldoffsetx, oldoffsety; - float oldwidth, oldheight; - int directions; -} NodeSizeWidget; - -static void node_resize_init( - bContext *C, wmOperator *op, const wmEvent *UNUSED(event), bNode *node, int dir) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - NodeSizeWidget *nsw = MEM_callocN(sizeof(NodeSizeWidget), "size widget op data"); - - op->customdata = nsw; - nsw->mxstart = snode->runtime->cursor[0] * UI_DPI_FAC; - nsw->mystart = snode->runtime->cursor[1] * UI_DPI_FAC; - - /* store old */ - nsw->oldlocx = node->locx; - nsw->oldlocy = node->locy; - nsw->oldoffsetx = node->offsetx; - nsw->oldoffsety = node->offsety; - nsw->oldwidth = node->width; - nsw->oldheight = node->height; - nsw->directions = dir; - - WM_cursor_modal_set(CTX_wm_window(C), node_get_resize_cursor(dir)); - /* add modal handler */ - WM_event_add_modal_handler(C, op); -} - -static void node_resize_exit(bContext *C, wmOperator *op, bool cancel) -{ - WM_cursor_modal_restore(CTX_wm_window(C)); - - /* Restore old data on cancel. */ - if (cancel) { - SpaceNode *snode = CTX_wm_space_node(C); - bNode *node = nodeGetActive(snode->edittree); - NodeSizeWidget *nsw = op->customdata; - - node->locx = nsw->oldlocx; - node->locy = nsw->oldlocy; - node->offsetx = nsw->oldoffsetx; - node->offsety = nsw->oldoffsety; - node->width = nsw->oldwidth; - node->height = nsw->oldheight; - } - - MEM_freeN(op->customdata); - op->customdata = NULL; -} - -static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - bNode *node = nodeGetActive(snode->edittree); - NodeSizeWidget *nsw = op->customdata; - - switch (event->type) { - case MOUSEMOVE: { - float mx, my; - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &mx, &my); - float dx = (mx - nsw->mxstart) / UI_DPI_FAC; - float dy = (my - nsw->mystart) / UI_DPI_FAC; - - if (node) { - float *pwidth = &node->width; - float oldwidth = nsw->oldwidth; - float widthmin = node->typeinfo->minwidth; - float widthmax = node->typeinfo->maxwidth; - - { - if (nsw->directions & NODE_RESIZE_RIGHT) { - *pwidth = oldwidth + dx; - CLAMP(*pwidth, widthmin, widthmax); - } - if (nsw->directions & NODE_RESIZE_LEFT) { - float locmax = nsw->oldlocx + oldwidth; - - node->locx = nsw->oldlocx + dx; - CLAMP(node->locx, locmax - widthmax, locmax - widthmin); - *pwidth = locmax - node->locx; - } - } - - /* height works the other way round ... */ - { - float heightmin = UI_DPI_FAC * node->typeinfo->minheight; - float heightmax = UI_DPI_FAC * node->typeinfo->maxheight; - if (nsw->directions & NODE_RESIZE_TOP) { - float locmin = nsw->oldlocy - nsw->oldheight; - - node->locy = nsw->oldlocy + dy; - CLAMP(node->locy, locmin + heightmin, locmin + heightmax); - node->height = node->locy - locmin; - } - if (nsw->directions & NODE_RESIZE_BOTTOM) { - node->height = nsw->oldheight - dy; - CLAMP(node->height, heightmin, heightmax); - } - } - - /* XXX make callback? */ - if (node->type == NODE_FRAME) { - /* keep the offset symmetric around center point */ - if (nsw->directions & NODE_RESIZE_LEFT) { - node->locx = nsw->oldlocx + 0.5f * dx; - node->offsetx = nsw->oldoffsetx + 0.5f * dx; - } - if (nsw->directions & NODE_RESIZE_RIGHT) { - node->locx = nsw->oldlocx + 0.5f * dx; - node->offsetx = nsw->oldoffsetx - 0.5f * dx; - } - if (nsw->directions & NODE_RESIZE_TOP) { - node->locy = nsw->oldlocy + 0.5f * dy; - node->offsety = nsw->oldoffsety + 0.5f * dy; - } - if (nsw->directions & NODE_RESIZE_BOTTOM) { - node->locy = nsw->oldlocy + 0.5f * dy; - node->offsety = nsw->oldoffsety - 0.5f * dy; - } - } - } - - ED_region_tag_redraw(region); - - break; - } - case LEFTMOUSE: - case MIDDLEMOUSE: - case RIGHTMOUSE: { - if (event->val == KM_RELEASE) { - node_resize_exit(C, op, false); - ED_node_post_apply_transform(C, snode->edittree); - - return OPERATOR_FINISHED; - } - if (event->val == KM_PRESS) { - node_resize_exit(C, op, true); - ED_region_tag_redraw(region); - - return OPERATOR_CANCELLED; - } - break; - } - } - - return OPERATOR_RUNNING_MODAL; -} - -static int node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - bNode *node = nodeGetActive(snode->edittree); - int dir; - - if (node) { - float cursor[2]; - - /* convert mouse coordinates to v2d space */ - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); - dir = node->typeinfo->resize_area_func(node, cursor[0], cursor[1]); - if (dir != 0) { - node_resize_init(C, op, event, node, dir); - return OPERATOR_RUNNING_MODAL; - } - } - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; -} - -static void node_resize_cancel(bContext *C, wmOperator *op) -{ - node_resize_exit(C, op, true); -} - -void NODE_OT_resize(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Resize Node"; - ot->idname = "NODE_OT_resize"; - ot->description = "Resize a node"; - - /* api callbacks */ - ot->invoke = node_resize_invoke; - ot->modal = node_resize_modal; - ot->poll = ED_operator_node_active; - ot->cancel = node_resize_cancel; - - /* flags */ - ot->flag = OPTYPE_BLOCKING; -} - -/* ********************** hidden sockets ******************** */ - -bool node_has_hidden_sockets(bNode *node) -{ - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (sock->flag & SOCK_HIDDEN) { - return true; - } - } - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - if (sock->flag & SOCK_HIDDEN) { - return true; - } - } - return false; -} - -void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set) -{ - if (set == 0) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - sock->flag &= ~SOCK_HIDDEN; - } - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - sock->flag &= ~SOCK_HIDDEN; - } - } - else { - /* hide unused sockets */ - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (sock->link == NULL) { - sock->flag |= SOCK_HIDDEN; - } - } - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - if (nodeCountSocketLinks(snode->edittree, sock) == 0) { - sock->flag |= SOCK_HIDDEN; - } - } - } -} - -/* checks snode->mouse position, and returns found node/socket */ -static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSocket *socket) -{ - const float node_socket_height = node_socket_calculate_height(socket); - const rctf multi_socket_rect = { - .xmin = socket->locx - NODE_SOCKSIZE * 4.0f, - .xmax = socket->locx + NODE_SOCKSIZE * 2.0f, - /*.xmax = socket->locx + NODE_SOCKSIZE * 5.5f - * would be the same behavior as for regular sockets. - * But keep it smaller because for multi-input socket you - * sometimes want to drag the link to the other side, if you may - * accidentally pick the wrong link otherwise. */ - .ymin = socket->locy - node_socket_height, - .ymax = socket->locy + node_socket_height, - }; - if (BLI_rctf_isect_pt(&multi_socket_rect, cursor[0], cursor[1])) { - return true; - } - return false; -} - -/* type is SOCK_IN and/or SOCK_OUT */ -int node_find_indicated_socket( - SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, const float cursor[2], int in_out) -{ - rctf rect; - - *nodep = NULL; - *sockp = NULL; - - /* check if we click in a socket */ - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - BLI_rctf_init_pt_radius(&rect, cursor, NODE_SOCKSIZE + 4); - - if (!(node->flag & NODE_HIDDEN)) { - /* extra padding inside and out - allow dragging on the text areas too */ - if (in_out == SOCK_IN) { - rect.xmax += NODE_SOCKSIZE; - rect.xmin -= NODE_SOCKSIZE * 4; - } - else if (in_out == SOCK_OUT) { - rect.xmax += NODE_SOCKSIZE * 4; - rect.xmin -= NODE_SOCKSIZE; - } - } - - if (in_out & SOCK_IN) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (!nodeSocketIsHidden(sock)) { - if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) { - if (cursor_isect_multi_input_socket(cursor, sock)) { - if (node == visible_node(snode, &rect)) { - *nodep = node; - *sockp = sock; - return 1; - } - } - } - else if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { - if (node == visible_node(snode, &rect)) { - *nodep = node; - *sockp = sock; - return 1; - } - } - } - } - } - if (in_out & SOCK_OUT) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - if (!nodeSocketIsHidden(sock)) { - if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { - if (node == visible_node(snode, &rect)) { - *nodep = node; - *sockp = sock; - return 1; - } - } - } - } - } - } - - return 0; -} - -/* ****************** Duplicate *********************** */ - -static void node_duplicate_reparent_recursive(bNode *node) -{ - bNode *parent; - - node->flag |= NODE_TEST; - - /* find first selected parent */ - for (parent = node->parent; parent; parent = parent->parent) { - if (parent->flag & SELECT) { - if (!(parent->flag & NODE_TEST)) { - node_duplicate_reparent_recursive(parent); - } - break; - } - } - /* reparent node copy to parent copy */ - if (parent) { - nodeDetachNode(node->new_node); - nodeAttachNode(node->new_node, parent->new_node); - } -} - -static int node_duplicate_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs"); - bool do_tag_update = false; - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - bNode *lastnode = ntree->nodes.last; - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->flag & SELECT) { - BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); - - /* To ensure redraws or re-renders happen. */ - ED_node_tag_update_id(snode->id); - } - - /* make sure we don't copy new nodes again! */ - if (node == lastnode) { - break; - } - } - - /* copy links between selected nodes - * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! - */ - bNodeLink *lastlink = ntree->links.last; - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - /* This creates new links between copied nodes. - * If keep_inputs is set, also copies input links from unselected (when fromnode==NULL)! - */ - if (link->tonode && (link->tonode->flag & NODE_SELECT) && - (keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT)))) { - bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink"); - newlink->flag = link->flag; - newlink->tonode = link->tonode->new_node; - newlink->tosock = link->tosock->new_sock; - if (link->fromnode && (link->fromnode->flag & NODE_SELECT)) { - newlink->fromnode = link->fromnode->new_node; - newlink->fromsock = link->fromsock->new_sock; - } - else { - /* input node not copied, this keeps the original input linked */ - newlink->fromnode = link->fromnode; - newlink->fromsock = link->fromsock; - } - - BLI_addtail(&ntree->links, newlink); - } - - /* make sure we don't copy new links again! */ - if (link == lastlink) { - break; - } - } - - /* clear flags for recursive depth-first iteration */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - node->flag &= ~NODE_TEST; - } - /* reparent copied nodes */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if ((node->flag & SELECT) && !(node->flag & NODE_TEST)) { - node_duplicate_reparent_recursive(node); - } - - /* only has to check old nodes */ - if (node == lastnode) { - break; - } - } - - /* deselect old nodes, select the copies instead */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->flag & SELECT) { - /* has been set during copy above */ - bNode *newnode = node->new_node; - - nodeSetSelected(node, false); - node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE); - nodeSetSelected(newnode, true); - newnode->flag &= ~NODE_ACTIVE_PREVIEW; - - do_tag_update |= (do_tag_update || node_connected_to_output(bmain, ntree, newnode)); - } - - /* make sure we don't copy new nodes again! */ - if (node == lastnode) { - break; - } - } - - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(C, snode); - if (do_tag_update) { - snode_dag_update(C, snode); - } - - return OPERATOR_FINISHED; -} - -void NODE_OT_duplicate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Duplicate Nodes"; - ot->description = "Duplicate selected nodes"; - ot->idname = "NODE_OT_duplicate"; - - /* api callbacks */ - ot->exec = node_duplicate_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean( - ot->srna, "keep_inputs", 0, "Keep Inputs", "Keep the input links to duplicated nodes"); -} - -bool ED_node_select_check(ListBase *lb) -{ - LISTBASE_FOREACH (bNode *, node, lb) { - if (node->flag & NODE_SELECT) { - return true; - } - } - - return false; -} - -void ED_node_select_all(ListBase *lb, int action) -{ - if (action == SEL_TOGGLE) { - if (ED_node_select_check(lb)) { - action = SEL_DESELECT; - } - else { - action = SEL_SELECT; - } - } - - LISTBASE_FOREACH (bNode *, node, lb) { - switch (action) { - case SEL_SELECT: - nodeSetSelected(node, true); - break; - case SEL_DESELECT: - nodeSetSelected(node, false); - break; - case SEL_INVERT: - nodeSetSelected(node, !(node->flag & SELECT)); - break; - } - } -} - -/* ******************************** */ -/* XXX some code needing updating to operators. */ - -/* goes over all scenes, reads render layers */ -static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - Scene *curscene = CTX_data_scene(C); - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - /* first tag scenes unread */ - LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { - scene->id.tag |= LIB_TAG_DOIT; - } - - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if ((node->type == CMP_NODE_R_LAYERS) || - (node->type == CMP_NODE_CRYPTOMATTE && node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER)) { - ID *id = node->id; - if (id == NULL) { - continue; - } - if (id->tag & LIB_TAG_DOIT) { - RE_ReadRenderResult(curscene, (Scene *)id); - ntreeCompositTagRender((Scene *)id); - id->tag &= ~LIB_TAG_DOIT; - } - } - } - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_read_viewlayers(wmOperatorType *ot) -{ - - ot->name = "Read View Layers"; - ot->idname = "NODE_OT_read_viewlayers"; - ot->description = "Read all render layers of all used scenes"; - - ot->exec = node_read_viewlayers_exec; - - ot->poll = composite_node_active; - - /* flags */ - ot->flag = 0; -} - -int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Scene *sce = CTX_data_scene(C); - - /* This is actually a test whether scene is used by the compositor or not. - * All the nodes are using same render result, so there is no need to do - * anything smart about check how exactly scene is used. */ - bNode *node = NULL; - LISTBASE_FOREACH (bNode *, node_iter, &sce->nodetree->nodes) { - if (node_iter->id == (ID *)sce) { - node = node_iter; - break; - } - } - - if (node) { - ViewLayer *view_layer = BLI_findlink(&sce->view_layers, node->custom1); - - if (view_layer) { - PointerRNA op_ptr; - - WM_operator_properties_create(&op_ptr, "RENDER_OT_render"); - RNA_string_set(&op_ptr, "layer", view_layer->name); - RNA_string_set(&op_ptr, "scene", sce->id.name + 2); - - /* to keep keypositions */ - sce->r.scemode |= R_NO_FRAME_UPDATE; - - WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr); - - WM_operator_properties_free(&op_ptr); - - return OPERATOR_FINISHED; - } - } - return OPERATOR_CANCELLED; -} - -void NODE_OT_render_changed(wmOperatorType *ot) -{ - ot->name = "Render Changed Layer"; - ot->idname = "NODE_OT_render_changed"; - ot->description = "Render current scene, when input node's layer has been changed"; - - ot->exec = node_render_changed_exec; - - ot->poll = composite_node_active; - - /* flags */ - ot->flag = 0; -} - -/* ****************** Hide operator *********************** */ - -static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag) -{ - int tot_eq = 0, tot_neq = 0; - - /* Toggles the flag on all selected nodes. - * If the flag is set on all nodes it is unset. - * If the flag is not set on all nodes, it is set. - */ - - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (node->flag & SELECT) { - - if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) { - continue; - } - if (toggle_flag == NODE_OPTIONS && - !(node->typeinfo->draw_buttons || node->typeinfo->draw_buttons_ex)) { - continue; - } - - if (node->flag & toggle_flag) { - tot_eq++; - } - else { - tot_neq++; - } - } - } - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (node->flag & SELECT) { - - if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) { - continue; - } - if (toggle_flag == NODE_OPTIONS && - !(node->typeinfo->draw_buttons || node->typeinfo->draw_buttons_ex)) { - continue; - } - - if ((tot_eq && tot_neq) || tot_eq == 0) { - node->flag |= toggle_flag; - } - else { - node->flag &= ~toggle_flag; - } - } - } -} - -static int node_hide_toggle_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - /* sanity checking (poll callback checks this already) */ - if ((snode == NULL) || (snode->edittree == NULL)) { - return OPERATOR_CANCELLED; - } - - node_flag_toggle_exec(snode, NODE_HIDDEN); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_hide_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Hide"; - ot->description = "Toggle hiding of selected nodes"; - ot->idname = "NODE_OT_hide_toggle"; - - /* callbacks */ - ot->exec = node_hide_toggle_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - /* sanity checking (poll callback checks this already) */ - if ((snode == NULL) || (snode->edittree == NULL)) { - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - node_flag_toggle_exec(snode, NODE_PREVIEW); - - snode_notify(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_preview_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle Node Preview"; - ot->description = "Toggle preview display for selected nodes"; - ot->idname = "NODE_OT_preview_toggle"; - - /* callbacks */ - ot->exec = node_preview_toggle_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - /* sanity checking (poll callback checks this already) */ - if ((snode == NULL) || (snode->edittree == NULL)) { - return OPERATOR_CANCELLED; - } - - node_flag_toggle_exec(snode, NODE_OPTIONS); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_options_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle Node Options"; - ot->description = "Toggle option buttons display for selected nodes"; - ot->idname = "NODE_OT_options_toggle"; - - /* callbacks */ - ot->exec = node_options_toggle_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - /* sanity checking (poll callback checks this already) */ - if ((snode == NULL) || (snode->edittree == NULL)) { - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - /* Toggle for all selected nodes */ - bool hidden = false; - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (node->flag & SELECT) { - if (node_has_hidden_sockets(node)) { - hidden = true; - break; - } - } - } - - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (node->flag & SELECT) { - node_set_hidden_sockets(snode, node, !hidden); - } - } - - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_hide_socket_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle Hidden Node Sockets"; - ot->description = "Toggle unused node socket display"; - ot->idname = "NODE_OT_hide_socket_toggle"; - - /* callbacks */ - ot->exec = node_socket_toggle_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Mute operator *********************** */ - -static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bool do_tag_update = false; - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - /* Only allow muting of nodes having a mute func! */ - if ((node->flag & SELECT) && node->typeinfo->update_internal_links) { - node->flag ^= NODE_MUTED; - snode_update(snode, node); - do_tag_update |= (do_tag_update || node_connected_to_output(bmain, snode->edittree, node)); - } - } - - snode_notify(C, snode); - if (do_tag_update) { - snode_dag_update(C, snode); - } - - return OPERATOR_FINISHED; -} - -void NODE_OT_mute_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle Node Mute"; - ot->description = "Toggle muting of the nodes"; - ot->idname = "NODE_OT_mute_toggle"; - - /* callbacks */ - ot->exec = node_mute_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Delete operator ******************* */ - -static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bool do_tag_update = false; - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { - if (node->flag & SELECT) { - do_tag_update |= (do_tag_update || node_connected_to_output(bmain, snode->edittree, node)); - nodeRemoveNode(bmain, snode->edittree, node, true); - } - } - - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(C, snode); - if (do_tag_update) { - snode_dag_update(C, snode); - } - - return OPERATOR_FINISHED; -} - -void NODE_OT_delete(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Delete"; - ot->description = "Delete selected nodes"; - ot->idname = "NODE_OT_delete"; - - /* api callbacks */ - ot->exec = node_delete_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Switch View ******************* */ - -static bool node_switch_view_poll(bContext *C) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - if (snode && snode->edittree) { - return true; - } - - return false; -} - -static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { - if (node->flag & SELECT) { - /* call the update function from the Switch View node */ - node->update = NODE_UPDATE_OPERATOR; - } - } - - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_switch_view_update(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Update Views"; - ot->description = "Update views of selected node"; - ot->idname = "NODE_OT_switch_view_update"; - - /* api callbacks */ - ot->exec = node_switch_view_exec; - ot->poll = node_switch_view_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Delete with reconnect ******************* */ -static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { - if (node->flag & SELECT) { - nodeInternalRelink(snode->edittree, node); - nodeRemoveNode(bmain, snode->edittree, node, true); - } - } - - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_delete_reconnect(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Delete with Reconnect"; - ot->description = "Delete nodes; will reconnect nodes as if deletion was muted"; - ot->idname = "NODE_OT_delete_reconnect"; - - /* api callbacks */ - ot->exec = node_delete_reconnect_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** File Output Add Socket ******************* */ - -static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - SpaceNode *snode = CTX_wm_space_node(C); - PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNodeTree *ntree = NULL; - bNode *node = NULL; - char file_path[MAX_NAME]; - - if (ptr.data) { - node = ptr.data; - ntree = (bNodeTree *)ptr.owner_id; - } - else if (snode && snode->edittree) { - ntree = snode->edittree; - node = nodeGetActive(snode->edittree); - } - - if (!node || node->type != CMP_NODE_OUTPUT_FILE) { - return OPERATOR_CANCELLED; - } - - RNA_string_get(op->ptr, "file_path", file_path); - ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format); - - snode_notify(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_output_file_add_socket(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add File Node Socket"; - ot->description = "Add a new input to a file output node"; - ot->idname = "NODE_OT_output_file_add_socket"; - - /* callbacks */ - ot->exec = node_output_file_add_socket_exec; - ot->poll = composite_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_string( - ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Subpath of the output file"); -} - -/* ****************** Multi File Output Remove Socket ******************* */ - -static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNodeTree *ntree = NULL; - bNode *node = NULL; - - if (ptr.data) { - node = ptr.data; - ntree = (bNodeTree *)ptr.owner_id; - } - else if (snode && snode->edittree) { - ntree = snode->edittree; - node = nodeGetActive(snode->edittree); - } - - if (!node || node->type != CMP_NODE_OUTPUT_FILE) { - return OPERATOR_CANCELLED; - } - - if (!ntreeCompositOutputFileRemoveActiveSocket(ntree, node)) { - return OPERATOR_CANCELLED; - } - - snode_notify(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove File Node Socket"; - ot->description = "Remove active input from a file output node"; - ot->idname = "NODE_OT_output_file_remove_active_socket"; - - /* callbacks */ - ot->exec = node_output_file_remove_active_socket_exec; - ot->poll = composite_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Multi File Output Move Socket ******************* */ - -static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNode *node = NULL; - - if (ptr.data) { - node = ptr.data; - } - else if (snode && snode->edittree) { - node = nodeGetActive(snode->edittree); - } - - if (!node || node->type != CMP_NODE_OUTPUT_FILE) { - return OPERATOR_CANCELLED; - } - - NodeImageMultiFile *nimf = node->storage; - - bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input); - if (!sock) { - return OPERATOR_CANCELLED; - } - - int direction = RNA_enum_get(op->ptr, "direction"); - - if (direction == 1) { - bNodeSocket *before = sock->prev; - if (!before) { - return OPERATOR_CANCELLED; - } - BLI_remlink(&node->inputs, sock); - BLI_insertlinkbefore(&node->inputs, before, sock); - nimf->active_input--; - } - else { - bNodeSocket *after = sock->next; - if (!after) { - return OPERATOR_CANCELLED; - } - BLI_remlink(&node->inputs, sock); - BLI_insertlinkafter(&node->inputs, after, sock); - nimf->active_input++; - } - - snode_notify(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_output_file_move_active_socket(wmOperatorType *ot) -{ - static const EnumPropertyItem direction_items[] = { - {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, {0, NULL, 0, NULL, NULL}}; - - /* identifiers */ - ot->name = "Move File Node Socket"; - ot->description = "Move the active input of a file output node up or down the list"; - ot->idname = "NODE_OT_output_file_move_active_socket"; - - /* callbacks */ - ot->exec = node_output_file_move_active_socket_exec; - ot->poll = composite_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", ""); -} - -/* ****************** Copy Node Color ******************* */ - -static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - - if (!ntree) { - return OPERATOR_CANCELLED; - } - bNode *node = nodeGetActive(ntree); - if (!node) { - return OPERATOR_CANCELLED; - } - - LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { - if (node_iter->flag & NODE_SELECT && node_iter != node) { - if (node->flag & NODE_CUSTOM_COLOR) { - node_iter->flag |= NODE_CUSTOM_COLOR; - copy_v3_v3(node_iter->color, node->color); - } - else { - node_iter->flag &= ~NODE_CUSTOM_COLOR; - } - } - } - - ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_node_copy_color(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Copy Color"; - ot->description = "Copy color to all selected nodes"; - ot->idname = "NODE_OT_node_copy_color"; - - /* api callbacks */ - ot->exec = node_copy_color_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Copy to clipboard ******************* */ - -static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - /* clear current clipboard */ - BKE_node_clipboard_clear(); - BKE_node_clipboard_init(ntree); - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->flag & SELECT) { - /* No ID refcounting, this node is virtual, - * detached from any actual Blender data currently. */ - bNode *new_node = BKE_node_copy_store_new_pointers( - NULL, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN); - BKE_node_clipboard_add_node(new_node); - } - } - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->flag & SELECT) { - bNode *new_node = node->new_node; - - /* ensure valid pointers */ - if (new_node->parent) { - /* parent pointer must be redirected to new node or detached if parent is - * not copied */ - if (new_node->parent->flag & NODE_SELECT) { - new_node->parent = new_node->parent->new_node; - } - else { - nodeDetachNode(new_node); - } - } - } - } - - /* copy links between selected nodes - * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! - */ - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - /* This creates new links between copied nodes. */ - if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode && - (link->fromnode->flag & NODE_SELECT)) { - bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink"); - newlink->flag = link->flag; - newlink->tonode = link->tonode->new_node; - newlink->tosock = link->tosock->new_sock; - newlink->fromnode = link->fromnode->new_node; - newlink->fromsock = link->fromsock->new_sock; - - BKE_node_clipboard_add_link(newlink); - } - } - - return OPERATOR_FINISHED; -} - -void NODE_OT_clipboard_copy(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Copy to Clipboard"; - ot->description = "Copies selected nodes to the clipboard"; - ot->idname = "NODE_OT_clipboard_copy"; - - /* api callbacks */ - ot->exec = node_clipboard_copy_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Paste from clipboard ******************* */ - -static int node_clipboard_paste_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - - /* validate pointers in the clipboard */ - bool is_clipboard_valid = BKE_node_clipboard_validate(); - const ListBase *clipboard_nodes_lb = BKE_node_clipboard_get_nodes(); - const ListBase *clipboard_links_lb = BKE_node_clipboard_get_links(); - - if (BLI_listbase_is_empty(clipboard_nodes_lb)) { - BKE_report(op->reports, RPT_ERROR, "Clipboard is empty"); - return OPERATOR_CANCELLED; - } - - if (BKE_node_clipboard_get_type() != ntree->type) { - BKE_report(op->reports, RPT_ERROR, "Clipboard nodes are an incompatible type"); - return OPERATOR_CANCELLED; - } - - /* only warn */ - if (is_clipboard_valid == false) { - BKE_report(op->reports, - RPT_WARNING, - "Some nodes references could not be restored, will be left empty"); - } - - /* make sure all clipboard nodes would be valid in the target tree */ - bool all_nodes_valid = true; - LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { - const char *disabled_hint = NULL; - if (!node->typeinfo->poll_instance || - !node->typeinfo->poll_instance(node, ntree, &disabled_hint)) { - all_nodes_valid = false; - if (disabled_hint) { - BKE_reportf(op->reports, - RPT_ERROR, - "Cannot add node %s into node tree %s:\n %s", - node->name, - ntree->id.name + 2, - disabled_hint); - } - else { - BKE_reportf(op->reports, - RPT_ERROR, - "Cannot add node %s into node tree %s", - node->name, - ntree->id.name + 2); - } - } - } - if (!all_nodes_valid) { - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - /* deselect old nodes */ - node_deselect_all(snode); - - /* calculate "barycenter" for placing on mouse cursor */ - float center[2] = {0.0f, 0.0f}; - int num_nodes = 0; - LISTBASE_FOREACH_INDEX (bNode *, node, clipboard_nodes_lb, num_nodes) { - center[0] += BLI_rctf_cent_x(&node->totr); - center[1] += BLI_rctf_cent_y(&node->totr); - } - mul_v2_fl(center, 1.0 / num_nodes); - - /* copy nodes from clipboard */ - LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { - bNode *new_node = BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); - - /* pasted nodes are selected */ - nodeSetSelected(new_node, true); - } - - /* reparent copied nodes */ - LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { - bNode *new_node = node->new_node; - if (new_node->parent) { - new_node->parent = new_node->parent->new_node; - } - } - - LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) { - nodeAddLink(ntree, - link->fromnode->new_node, - link->fromsock->new_sock, - link->tonode->new_node, - link->tosock->new_sock); - } - - Main *bmain = CTX_data_main(C); - ntreeUpdateTree(bmain, snode->edittree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - /* Pasting nodes can create arbitrary new relations, because nodes can reference IDs. */ - DEG_relations_tag_update(bmain); - - return OPERATOR_FINISHED; -} - -void NODE_OT_clipboard_paste(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Paste from Clipboard"; - ot->description = "Pastes nodes from the clipboard to the active node tree"; - ot->idname = "NODE_OT_clipboard_paste"; - - /* api callbacks */ - ot->exec = node_clipboard_paste_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/********************** Add interface socket operator *********************/ - -static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb) -{ - LISTBASE_FOREACH (bNodeSocket *, socket, lb) { - if (socket->flag & SELECT) { - return socket; - } - } - return NULL; -} - -static int ntree_socket_add_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - - PointerRNA ntree_ptr; - RNA_id_pointer_create((ID *)ntree, &ntree_ptr); - - const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out"); - ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs; - - const char *default_name = (in_out == SOCK_IN) ? "Input" : "Output"; - bNodeSocket *active_sock = ntree_get_active_interface_socket(sockets); - - bNodeSocket *sock; - if (active_sock) { - /* insert a copy of the active socket right after it */ - sock = ntreeInsertSocketInterface( - ntree, in_out, active_sock->idname, active_sock->next, active_sock->name); - /* XXX this only works for actual sockets, not interface templates! */ - /*nodeSocketCopyValue(sock, &ntree_ptr, active_sock, &ntree_ptr);*/ - } - else { - /* XXX TODO define default socket type for a tree! */ - sock = ntreeAddSocketInterface(ntree, in_out, "NodeSocketFloat", default_name); - } - - /* Deactivate sockets. */ - LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { - socket_iter->flag &= ~SELECT; - } - /* make the new socket active */ - sock->flag |= SELECT; - - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_tree_socket_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Node Tree Interface Socket"; - ot->description = "Add an input or output socket to the current node tree"; - ot->idname = "NODE_OT_tree_socket_add"; - - /* api callbacks */ - ot->exec = ntree_socket_add_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); -} - -/********************** Remove interface socket operator *********************/ - -static int ntree_socket_remove_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out"); - - bNodeSocket *iosock = ntree_get_active_interface_socket(in_out == SOCK_IN ? &ntree->inputs : - &ntree->outputs); - if (iosock == NULL) { - return OPERATOR_CANCELLED; - } - - /* preferably next socket becomes active, otherwise try previous socket */ - bNodeSocket *active_sock = (iosock->next ? iosock->next : iosock->prev); - ntreeRemoveSocketInterface(ntree, iosock); - - /* set active socket */ - if (active_sock) { - active_sock->flag |= SELECT; - } - - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_tree_socket_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Node Tree Interface Socket"; - ot->description = "Remove an input or output socket to the current node tree"; - ot->idname = "NODE_OT_tree_socket_remove"; - - /* api callbacks */ - ot->exec = ntree_socket_remove_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); -} - -/********************** Move interface socket operator *********************/ - -static const EnumPropertyItem move_direction_items[] = { - {1, "UP", 0, "Up", ""}, - {2, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL}, -}; - -static int ntree_socket_move_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - int direction = RNA_enum_get(op->ptr, "direction"); - - const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out"); - ListBase *sockets = in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs; - - bNodeSocket *iosock = ntree_get_active_interface_socket(sockets); - - if (iosock == NULL) { - return OPERATOR_CANCELLED; - } - - switch (direction) { - case 1: { /* up */ - bNodeSocket *before = iosock->prev; - BLI_remlink(sockets, iosock); - if (before) { - BLI_insertlinkbefore(sockets, before, iosock); - } - else { - BLI_addhead(sockets, iosock); - } - break; - } - case 2: { /* down */ - bNodeSocket *after = iosock->next; - BLI_remlink(sockets, iosock); - if (after) { - BLI_insertlinkafter(sockets, after, iosock); - } - else { - BLI_addtail(sockets, iosock); - } - break; - } - } - - ntree->update |= NTREE_UPDATE_GROUP; - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_tree_socket_move(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Move Node Tree Socket"; - ot->description = "Move a socket up or down in the current node tree's sockets stack"; - ot->idname = "NODE_OT_tree_socket_move"; - - /* api callbacks */ - ot->exec = ntree_socket_move_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "direction", move_direction_items, 1, "Direction", ""); - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); -} - -/* ********************** Shader Script Update ******************/ - -static bool node_shader_script_update_poll(bContext *C) -{ - Scene *scene = CTX_data_scene(C); - const RenderEngineType *type = RE_engines_find(scene->r.engine); - SpaceNode *snode = CTX_wm_space_node(C); - - /* test if we have a render engine that supports shaders scripts */ - if (!(type && type->update_script_node)) { - return 0; - } - - /* see if we have a shader script node in context */ - bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data; - - if (!node && snode && snode->edittree) { - node = nodeGetActive(snode->edittree); - } - - if (node && node->type == SH_NODE_SCRIPT) { - NodeShaderScript *nss = node->storage; - - if (node->id || nss->filepath[0]) { - return ED_operator_node_editable(C); - } - } - - /* see if we have a text datablock in context */ - Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; - if (text) { - return 1; - } - - /* we don't check if text datablock is actually in use, too slow for poll */ - - return 0; -} - -/* recursively check for script nodes in groups using this text and update */ -static bool node_shader_script_update_text_recursive(RenderEngine *engine, - RenderEngineType *type, - bNodeTree *ntree, - Text *text) -{ - bool found = false; - - ntree->done = true; - - /* update each script that is using this text datablock */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == NODE_GROUP) { - bNodeTree *ngroup = (bNodeTree *)node->id; - if (ngroup && !ngroup->done) { - found |= node_shader_script_update_text_recursive(engine, type, ngroup, text); - } - } - else if (node->type == SH_NODE_SCRIPT && node->id == &text->id) { - type->update_script_node(engine, ntree, node); - found = true; - } - } - - return found; -} - -static int node_shader_script_update_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - SpaceNode *snode = CTX_wm_space_node(C); - PointerRNA nodeptr = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript); - bool found = false; - - /* setup render engine */ - RenderEngineType *type = RE_engines_find(scene->r.engine); - RenderEngine *engine = RE_engine_create(type); - engine->reports = op->reports; - - /* get node */ - bNodeTree *ntree_base = NULL; - bNode *node = NULL; - if (nodeptr.data) { - ntree_base = (bNodeTree *)nodeptr.owner_id; - node = nodeptr.data; - } - else if (snode && snode->edittree) { - ntree_base = snode->edittree; - node = nodeGetActive(snode->edittree); - } - - if (node) { - /* update single node */ - type->update_script_node(engine, ntree_base, node); - - found = true; - } - else { - /* update all nodes using text datablock */ - Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; - - if (text) { - /* clear flags for recursion check */ - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_SHADER) { - ntree->done = false; - } - } - FOREACH_NODETREE_END; - - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_SHADER) { - if (!ntree->done) { - found |= node_shader_script_update_text_recursive(engine, type, ntree, text); - } - } - } - FOREACH_NODETREE_END; - - if (!found) { - BKE_report(op->reports, RPT_INFO, "Text not used by any node, no update done"); - } - } - } - - RE_engine_free(engine); - - return (found) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; -} - -void NODE_OT_shader_script_update(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Script Node Update"; - ot->description = "Update shader script node with new sockets and options from the script"; - ot->idname = "NODE_OT_shader_script_update"; - - /* api callbacks */ - ot->exec = node_shader_script_update_exec; - ot->poll = node_shader_script_update_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ********************** Viewer border ******************/ - -static void viewer_border_corner_to_backdrop(SpaceNode *snode, - ARegion *region, - int x, - int y, - int backdrop_width, - int backdrop_height, - float *fx, - float *fy) -{ - float bufx = backdrop_width * snode->zoom; - float bufy = backdrop_height * snode->zoom; - - *fx = (bufx > 0.0f ? ((float)x - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f); - *fy = (bufy > 0.0f ? ((float)y - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f); -} - -static int viewer_border_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - void *lock; - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); - - if (ibuf) { - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *btree = snode->nodetree; - rcti rect; - rctf rectf; - - /* get border from operator */ - WM_operator_properties_border_to_rcti(op, &rect); - - /* convert border to unified space within backdrop image */ - viewer_border_corner_to_backdrop( - snode, region, rect.xmin, rect.ymin, ibuf->x, ibuf->y, &rectf.xmin, &rectf.ymin); - - viewer_border_corner_to_backdrop( - snode, region, rect.xmax, rect.ymax, ibuf->x, ibuf->y, &rectf.xmax, &rectf.ymax); - - /* clamp coordinates */ - rectf.xmin = max_ff(rectf.xmin, 0.0f); - rectf.ymin = max_ff(rectf.ymin, 0.0f); - rectf.xmax = min_ff(rectf.xmax, 1.0f); - rectf.ymax = min_ff(rectf.ymax, 1.0f); - - if (rectf.xmin < rectf.xmax && rectf.ymin < rectf.ymax) { - btree->viewer_border = rectf; - - if (rectf.xmin == 0.0f && rectf.ymin == 0.0f && rectf.xmax == 1.0f && rectf.ymax == 1.0f) { - btree->flag &= ~NTREE_VIEWER_BORDER; - } - else { - btree->flag |= NTREE_VIEWER_BORDER; - } - - snode_notify(C, snode); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - } - else { - btree->flag &= ~NTREE_VIEWER_BORDER; - } - } - - BKE_image_release_ibuf(ima, ibuf, lock); - - return OPERATOR_FINISHED; -} - -void NODE_OT_viewer_border(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Viewer Region"; - ot->description = "Set the boundaries for viewer operations"; - ot->idname = "NODE_OT_viewer_border"; - - /* api callbacks */ - ot->invoke = WM_gesture_box_invoke; - ot->exec = viewer_border_exec; - ot->modal = WM_gesture_box_modal; - ot->cancel = WM_gesture_box_cancel; - ot->poll = composite_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - WM_operator_properties_gesture_box(ot); -} - -static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *btree = snode->nodetree; - - btree->flag &= ~NTREE_VIEWER_BORDER; - snode_notify(C, snode); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_clear_viewer_border(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Clear Viewer Region"; - ot->description = "Clear the boundaries for viewer operations"; - ot->idname = "NODE_OT_clear_viewer_border"; - - /* api callbacks */ - ot->exec = clear_viewer_border_exec; - ot->poll = composite_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Cryptomatte Add Socket ******************* */ - -static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNodeTree *ntree = NULL; - bNode *node = NULL; - - if (ptr.data) { - node = ptr.data; - ntree = (bNodeTree *)ptr.owner_id; - } - else if (snode && snode->edittree) { - ntree = snode->edittree; - node = nodeGetActive(snode->edittree); - } - - if (!node || node->type != CMP_NODE_CRYPTOMATTE_LEGACY) { - return OPERATOR_CANCELLED; - } - - ntreeCompositCryptomatteAddSocket(ntree, node); - - snode_notify(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Cryptomatte Socket"; - ot->description = "Add a new input layer to a Cryptomatte node"; - ot->idname = "NODE_OT_cryptomatte_layer_add"; - - /* callbacks */ - ot->exec = node_cryptomatte_add_socket_exec; - ot->poll = composite_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Cryptomatte Remove Socket ******************* */ - -static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - PointerRNA ptr = CTX_data_pointer_get(C, "node"); - bNodeTree *ntree = NULL; - bNode *node = NULL; - - if (ptr.data) { - node = ptr.data; - ntree = (bNodeTree *)ptr.owner_id; - } - else if (snode && snode->edittree) { - ntree = snode->edittree; - node = nodeGetActive(snode->edittree); - } - - if (!node || node->type != CMP_NODE_CRYPTOMATTE_LEGACY) { - return OPERATOR_CANCELLED; - } - - if (!ntreeCompositCryptomatteRemoveSocket(ntree, node)) { - return OPERATOR_CANCELLED; - } - - snode_notify(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Cryptomatte Socket"; - ot->description = "Remove layer from a Cryptomatte node"; - ot->idname = "NODE_OT_cryptomatte_layer_remove"; - - /* callbacks */ - ot->exec = node_cryptomatte_remove_socket_exec; - ot->poll = composite_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc new file mode 100644 index 00000000000..b98a3fcd59b --- /dev/null +++ b/source/blender/editors/space_node/node_edit.cc @@ -0,0 +1,2835 @@ +/* + * 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. + */ + +/** \file + * \ingroup spnode + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_light_types.h" +#include "DNA_material_types.h" +#include "DNA_node_types.h" +#include "DNA_text_types.h" +#include "DNA_world_types.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_node.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_workspace.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "RE_engine.h" +#include "RE_pipeline.h" + +#include "ED_node.h" /* own include */ +#include "ED_render.h" +#include "ED_screen.h" +#include "ED_select_utils.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_view2d.h" + +#include "GPU_material.h" + +#include "IMB_imbuf_types.h" + +#include "NOD_composite.h" +#include "NOD_geometry.h" +#include "NOD_shader.h" +#include "NOD_texture.h" +#include "node_intern.h" /* own include */ + +#define USE_ESC_COMPO + +/* ***************** composite job manager ********************** */ + +enum { + COM_RECALC_COMPOSITE = 1, + COM_RECALC_VIEWER = 2, +}; + +struct CompoJob { + /* Input parameters. */ + Main *bmain; + Scene *scene; + ViewLayer *view_layer; + bNodeTree *ntree; + int recalc_flags; + /* Evaluated state/ */ + Depsgraph *compositor_depsgraph; + bNodeTree *localtree; + /* Jon system integration. */ + const short *stop; + short *do_update; + float *progress; +}; + +float node_socket_calculate_height(const bNodeSocket *socket) +{ + float sock_height = NODE_SOCKSIZE * 2.0f; + if (socket->flag & SOCK_MULTI_INPUT) { + sock_height += max_ii(NODE_MULTI_INPUT_LINK_GAP * 0.5f * socket->total_inputs, NODE_SOCKSIZE); + } + return sock_height; +} + +void node_link_calculate_multi_input_position(const float socket_x, + const float socket_y, + const int index, + const int total_inputs, + float r[2]) +{ + float offset = (total_inputs * NODE_MULTI_INPUT_LINK_GAP - NODE_MULTI_INPUT_LINK_GAP) * 0.5; + r[0] = socket_x - NODE_SOCKSIZE * 0.5f; + r[1] = socket_y - offset + (index * NODE_MULTI_INPUT_LINK_GAP); +} + +static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags) +{ + LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) { + if (node->type == CMP_NODE_COMPOSITE) { + if (recalc_flags & COM_RECALC_COMPOSITE) { + node->flag |= NODE_DO_OUTPUT_RECALC; + } + } + else if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { + if (recalc_flags & COM_RECALC_VIEWER) { + node->flag |= NODE_DO_OUTPUT_RECALC; + } + } + else if (node->type == NODE_GROUP) { + if (node->id) { + compo_tag_output_nodes((bNodeTree *)node->id, recalc_flags); + } + } + } +} + +static int compo_get_recalc_flags(const bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + int recalc_flags = 0; + + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + const bScreen *screen = WM_window_get_active_screen(win); + + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)area->spacedata.first; + if (sima->image) { + if (sima->image->type == IMA_TYPE_R_RESULT) { + recalc_flags |= COM_RECALC_COMPOSITE; + } + else if (sima->image->type == IMA_TYPE_COMPOSITE) { + recalc_flags |= COM_RECALC_VIEWER; + } + } + } + else if (area->spacetype == SPACE_NODE) { + SpaceNode *snode = (SpaceNode *)area->spacedata.first; + if (snode->flag & SNODE_BACKDRAW) { + recalc_flags |= COM_RECALC_VIEWER; + } + } + } + } + + return recalc_flags; +} + +/* called by compo, only to check job 'stop' value */ +static int compo_breakjob(void *cjv) +{ + CompoJob *cj = (CompoJob *)cjv; + + /* without G.is_break 'ESC' wont quit - which annoys users */ + return (*(cj->stop) +#ifdef USE_ESC_COMPO + || G.is_break +#endif + ); +} + +/* called by compo, wmJob sends notifier */ +static void compo_statsdrawjob(void *cjv, const char *UNUSED(str)) +{ + CompoJob *cj = (CompoJob *)cjv; + + *(cj->do_update) = true; +} + +/* called by compo, wmJob sends notifier */ +static void compo_redrawjob(void *cjv) +{ + CompoJob *cj = (CompoJob *)cjv; + + *(cj->do_update) = true; +} + +static void compo_freejob(void *cjv) +{ + CompoJob *cj = (CompoJob *)cjv; + + if (cj->localtree) { + ntreeLocalMerge(cj->bmain, cj->localtree, cj->ntree); + } + if (cj->compositor_depsgraph != nullptr) { + DEG_graph_free(cj->compositor_depsgraph); + } + MEM_freeN(cj); +} + +/* only now we copy the nodetree, so adding many jobs while + * sliding buttons doesn't frustrate */ +static void compo_initjob(void *cjv) +{ + CompoJob *cj = (CompoJob *)cjv; + Main *bmain = cj->bmain; + Scene *scene = cj->scene; + ViewLayer *view_layer = cj->view_layer; + + cj->compositor_depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); + DEG_graph_build_for_compositor_preview(cj->compositor_depsgraph, cj->ntree); + + /* NOTE: Don't update animation to preserve unkeyed changes, this means can not use + * evaluate_on_framechange. */ + DEG_evaluate_on_refresh(cj->compositor_depsgraph); + + bNodeTree *ntree_eval = (bNodeTree *)DEG_get_evaluated_id(cj->compositor_depsgraph, + &cj->ntree->id); + + cj->localtree = ntreeLocalize(ntree_eval); + + if (cj->recalc_flags) { + compo_tag_output_nodes(cj->localtree, cj->recalc_flags); + } +} + +/* called before redraw notifiers, it moves finished previews over */ +static void compo_updatejob(void *UNUSED(cjv)) +{ + WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, nullptr); +} + +static void compo_progressjob(void *cjv, float progress) +{ + CompoJob *cj = (CompoJob *)cjv; + + *(cj->progress) = progress; +} + +/* only this runs inside thread */ +static void compo_startjob(void *cjv, + /* Cannot be const, this function implements wm_jobs_start_callback. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + short *stop, + short *do_update, + float *progress) +{ + CompoJob *cj = (CompoJob *)cjv; + bNodeTree *ntree = cj->localtree; + Scene *scene = cj->scene; + + if (scene->use_nodes == false) { + return; + } + + cj->stop = stop; + cj->do_update = do_update; + cj->progress = progress; + + ntree->test_break = compo_breakjob; + ntree->tbh = cj; + ntree->stats_draw = compo_statsdrawjob; + ntree->sdh = cj; + ntree->progress = compo_progressjob; + ntree->prh = cj; + ntree->update_draw = compo_redrawjob; + ntree->udh = cj; + + // XXX BIF_store_spare(); + /* 1 is do_previews */ + + if ((cj->scene->r.scemode & R_MULTIVIEW) == 0) { + ntreeCompositExecTree(cj->scene, + ntree, + &cj->scene->r, + false, + true, + &scene->view_settings, + &scene->display_settings, + ""); + } + else { + LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) { + continue; + } + ntreeCompositExecTree(cj->scene, + ntree, + &cj->scene->r, + false, + true, + &scene->view_settings, + &scene->display_settings, + srv->name); + } + } + + ntree->test_break = nullptr; + ntree->stats_draw = nullptr; + ntree->progress = nullptr; +} + +/** + * \param scene_owner: is the owner of the job, + * we don't use it for anything else currently so could also be a void pointer, + * but for now keep it an 'Scene' for consistency. + * + * \note only call from spaces `refresh` callbacks, not direct! - use with care. + */ +void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + /* to fix bug: T32272. */ + if (G.is_rendering) { + return; + } + +#ifdef USE_ESC_COMPO + G.is_break = false; +#endif + + BKE_image_backup_render( + scene, BKE_image_ensure_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"), false); + + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + scene_owner, + "Compositing", + WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS, + WM_JOB_TYPE_COMPOSITE); + CompoJob *cj = (CompoJob *)MEM_callocN(sizeof(CompoJob), "compo job"); + + /* customdata for preview thread */ + cj->bmain = bmain; + cj->scene = scene; + cj->view_layer = view_layer; + cj->ntree = nodetree; + cj->recalc_flags = compo_get_recalc_flags(C); + + /* setup job */ + WM_jobs_customdata_set(wm_job, cj, compo_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_COMPO_RESULT, NC_SCENE | ND_COMPO_RESULT); + WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, nullptr); + + WM_jobs_start(CTX_wm_manager(C), wm_job); +} + +/* ***************************************** */ + +/* operator poll callback */ +bool composite_node_active(bContext *C) +{ + if (ED_operator_node_active(C)) { + SpaceNode *snode = CTX_wm_space_node(C); + if (ED_node_is_compositor(snode)) { + return true; + } + } + return false; +} + +/* operator poll callback */ +bool composite_node_editable(bContext *C) +{ + if (ED_operator_node_editable(C)) { + SpaceNode *snode = CTX_wm_space_node(C); + if (ED_node_is_compositor(snode)) { + return true; + } + } + return false; +} + +void snode_dag_update(bContext *C, SpaceNode *snode) +{ + Main *bmain = CTX_data_main(C); + + /* for groups, update all ID's using this */ + if (snode->edittree != snode->nodetree) { + FOREACH_NODETREE_BEGIN (bmain, tntree, id) { + if (ntreeHasTree(tntree, snode->edittree)) { + DEG_id_tag_update(id, 0); + } + } + FOREACH_NODETREE_END; + } + + DEG_id_tag_update(snode->id, 0); + DEG_id_tag_update(&snode->nodetree->id, 0); +} + +void snode_notify(bContext *C, SpaceNode *snode) +{ + ID *id = snode->id; + + WM_event_add_notifier(C, NC_NODE | NA_EDITED, nullptr); + + if (ED_node_is_shader(snode)) { + if (GS(id->name) == ID_MA) { + WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id); + } + else if (GS(id->name) == ID_LA) { + WM_main_add_notifier(NC_LAMP | ND_LIGHTING, id); + } + else if (GS(id->name) == ID_WO) { + WM_main_add_notifier(NC_WORLD | ND_WORLD, id); + } + } + else if (ED_node_is_compositor(snode)) { + WM_event_add_notifier(C, NC_SCENE | ND_NODES, id); + } + else if (ED_node_is_texture(snode)) { + WM_event_add_notifier(C, NC_TEXTURE | ND_NODES, id); + } + else if (ED_node_is_geometry(snode)) { + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id); + } +} + +void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo) +{ + if (typeinfo) { + BLI_strncpy(snode->tree_idname, typeinfo->idname, sizeof(snode->tree_idname)); + } + else { + snode->tree_idname[0] = '\0'; + } +} + +bool ED_node_is_compositor(struct SpaceNode *snode) +{ + return STREQ(snode->tree_idname, ntreeType_Composite->idname); +} + +bool ED_node_is_shader(struct SpaceNode *snode) +{ + return STREQ(snode->tree_idname, ntreeType_Shader->idname); +} + +bool ED_node_is_texture(struct SpaceNode *snode) +{ + return STREQ(snode->tree_idname, ntreeType_Texture->idname); +} + +bool ED_node_is_geometry(struct SpaceNode *snode) +{ + return STREQ(snode->tree_idname, ntreeType_Geometry->idname); +} + +/* assumes nothing being done in ntree yet, sets the default in/out node */ +/* called from shading buttons or header */ +void ED_node_shader_default(const bContext *C, ID *id) +{ + Main *bmain = CTX_data_main(C); + + if (GS(id->name) == ID_MA) { + /* Materials */ + Object *ob = CTX_data_active_object(C); + Material *ma = (Material *)id; + Material *ma_default; + + if (ob && ob->type == OB_VOLUME) { + ma_default = BKE_material_default_volume(); + } + else { + ma_default = BKE_material_default_surface(); + } + + ma->nodetree = ntreeCopyTree(bmain, ma_default->nodetree); + ntreeUpdateTree(bmain, ma->nodetree); + } + else if (ELEM(GS(id->name), ID_WO, ID_LA)) { + /* Emission */ + bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname); + bNode *shader, *output; + + if (GS(id->name) == ID_WO) { + World *world = (World *)id; + world->nodetree = ntree; + + shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_BACKGROUND); + output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_WORLD); + nodeAddLink(ntree, + shader, + nodeFindSocket(shader, SOCK_OUT, "Background"), + output, + nodeFindSocket(output, SOCK_IN, "Surface")); + + bNodeSocket *color_sock = nodeFindSocket(shader, SOCK_IN, "Color"); + copy_v3_v3(((bNodeSocketValueRGBA *)color_sock->default_value)->value, &world->horr); + } + else { + Light *light = (Light *)id; + light->nodetree = ntree; + + shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION); + output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_LIGHT); + nodeAddLink(ntree, + shader, + nodeFindSocket(shader, SOCK_OUT, "Emission"), + output, + nodeFindSocket(output, SOCK_IN, "Surface")); + } + + shader->locx = 10.0f; + shader->locy = 300.0f; + output->locx = 300.0f; + output->locy = 300.0f; + nodeSetActive(ntree, output); + ntreeUpdateTree(bmain, ntree); + } + else { + printf("ED_node_shader_default called on wrong ID type.\n"); + return; + } +} + +/* assumes nothing being done in ntree yet, sets the default in/out node */ +/* called from shading buttons or header */ +void ED_node_composit_default(const bContext *C, struct Scene *sce) +{ + /* but lets check it anyway */ + if (sce->nodetree) { + if (G.debug & G_DEBUG) { + printf("error in composite initialize\n"); + } + return; + } + + sce->nodetree = ntreeAddTree(nullptr, "Compositing Nodetree", ntreeType_Composite->idname); + + sce->nodetree->chunksize = 256; + sce->nodetree->edit_quality = NTREE_QUALITY_HIGH; + sce->nodetree->render_quality = NTREE_QUALITY_HIGH; + + bNode *out = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_COMPOSITE); + out->locx = 300.0f; + out->locy = 400.0f; + + bNode *in = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_R_LAYERS); + in->locx = 10.0f; + in->locy = 400.0f; + nodeSetActive(sce->nodetree, in); + + /* links from color to color */ + bNodeSocket *fromsock = (bNodeSocket *)in->outputs.first; + bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; + nodeAddLink(sce->nodetree, in, fromsock, out, tosock); + + ntreeUpdateTree(CTX_data_main(C), sce->nodetree); +} + +/* assumes nothing being done in ntree yet, sets the default in/out node */ +/* called from shading buttons or header */ +void ED_node_texture_default(const bContext *C, Tex *tex) +{ + /* but lets check it anyway */ + if (tex->nodetree) { + if (G.debug & G_DEBUG) { + printf("error in texture initialize\n"); + } + return; + } + + tex->nodetree = ntreeAddTree(nullptr, "Texture Nodetree", ntreeType_Texture->idname); + + bNode *out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT); + out->locx = 300.0f; + out->locy = 300.0f; + + bNode *in = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_CHECKER); + in->locx = 10.0f; + in->locy = 300.0f; + nodeSetActive(tex->nodetree, in); + + bNodeSocket *fromsock = (bNodeSocket *)in->outputs.first; + bNodeSocket *tosock = (bNodeSocket *)out->inputs.first; + nodeAddLink(tex->nodetree, in, fromsock, out, tosock); + + ntreeUpdateTree(CTX_data_main(C), tex->nodetree); +} + +/* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */ +void snode_set_context(const bContext *C) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTreeType *treetype = ntreeTypeFind(snode->tree_idname); + bNodeTree *ntree = snode->nodetree; + ID *id = snode->id, *from = snode->from; + + /* check the tree type */ + if (!treetype || (treetype->poll && !treetype->poll(C, treetype))) { + /* invalid tree type, skip + * NB: not resetting the node path here, invalid bNodeTreeType + * may still be registered at a later point. + */ + return; + } + + if (snode->nodetree && !STREQ(snode->nodetree->idname, snode->tree_idname)) { + /* current tree does not match selected type, clear tree path */ + ntree = nullptr; + id = nullptr; + from = nullptr; + } + + if (!(snode->flag & SNODE_PIN) || ntree == nullptr) { + if (treetype->get_from_context) { + /* reset and update from context */ + ntree = nullptr; + id = nullptr; + from = nullptr; + + treetype->get_from_context(C, treetype, &ntree, &id, &from); + } + } + + if (snode->nodetree != ntree || snode->id != id || snode->from != from || + (snode->treepath.last == nullptr && ntree)) { + ED_node_tree_start(snode, ntree, id, from); + } +} + +void snode_update(SpaceNode *snode, bNode *node) +{ + /* XXX this only updates nodes in the current node space tree path. + * The function supposedly should update any potential group node linking to changed tree, + * this really requires a working depsgraph ... + */ + + /* update all edited group nodes */ + bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last; + if (path) { + bNodeTree *ngroup = path->nodetree; + for (path = path->prev; path; path = path->prev) { + nodeUpdateID(path->nodetree, (ID *)ngroup); + ngroup = path->nodetree; + } + } + + if (node) { + nodeUpdate(snode->edittree, node); + } +} + +void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed) +{ + const bool was_active_texture = (node->flag & NODE_ACTIVE_TEXTURE) != 0; + if (r_active_texture_changed) { + *r_active_texture_changed = false; + } + + nodeSetActive(ntree, node); + + if (node->type != NODE_GROUP) { + const bool was_output = (node->flag & NODE_DO_OUTPUT) != 0; + bool do_update = false; + + /* generic node group output: set node as active output */ + if (node->type == NODE_GROUP_OUTPUT) { + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (node_iter->type == NODE_GROUP_OUTPUT) { + node_iter->flag &= ~NODE_DO_OUTPUT; + } + } + + node->flag |= NODE_DO_OUTPUT; + if (!was_output) { + do_update = true; + } + } + + /* tree specific activate calls */ + if (ntree->type == NTREE_SHADER) { + /* when we select a material, active texture is cleared, for buttons */ + if (node->id && ELEM(GS(node->id->name), ID_MA, ID_LA, ID_WO)) { + nodeClearActiveID(ntree, ID_TE); + } + + if (ELEM(node->type, + SH_NODE_OUTPUT_MATERIAL, + SH_NODE_OUTPUT_WORLD, + SH_NODE_OUTPUT_LIGHT, + SH_NODE_OUTPUT_LINESTYLE)) { + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (node_iter->type == node->type) { + node_iter->flag &= ~NODE_DO_OUTPUT; + } + } + + node->flag |= NODE_DO_OUTPUT; + if (was_output == 0) { + ED_node_tag_update_nodetree(bmain, ntree, node); + } + } + else if (do_update) { + ED_node_tag_update_nodetree(bmain, ntree, node); + } + + /* if active texture changed, free glsl materials */ + if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) { + LISTBASE_FOREACH (Material *, ma, &bmain->materials) { + if (ma->nodetree && ma->use_nodes && ntreeHasTree(ma->nodetree, ntree)) { + GPU_material_free(&ma->gpumaterial); + } + } + + LISTBASE_FOREACH (World *, wo, &bmain->worlds) { + if (wo->nodetree && wo->use_nodes && ntreeHasTree(wo->nodetree, ntree)) { + GPU_material_free(&wo->gpumaterial); + } + } + + if (r_active_texture_changed) { + *r_active_texture_changed = true; + } + ED_node_tag_update_nodetree(bmain, ntree, node); + WM_main_add_notifier(NC_IMAGE, nullptr); + } + + WM_main_add_notifier(NC_MATERIAL | ND_NODES, node->id); + } + else if (ntree->type == NTREE_COMPOSIT) { + /* make active viewer, currently only 1 supported... */ + if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (ELEM(node_iter->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { + node_iter->flag &= ~NODE_DO_OUTPUT; + } + } + + node->flag |= NODE_DO_OUTPUT; + if (was_output == 0) { + ED_node_tag_update_nodetree(bmain, ntree, node); + } + + /* addnode() doesn't link this yet... */ + node->id = (ID *)BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + } + else if (node->type == CMP_NODE_COMPOSITE) { + if (was_output == 0) { + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (node_iter->type == CMP_NODE_COMPOSITE) { + node_iter->flag &= ~NODE_DO_OUTPUT; + } + } + + node->flag |= NODE_DO_OUTPUT; + ED_node_tag_update_nodetree(bmain, ntree, node); + } + } + else if (do_update) { + ED_node_tag_update_nodetree(bmain, ntree, node); + } + } + else if (ntree->type == NTREE_TEXTURE) { + /* XXX */ +#if 0 + if (node->id) { + BIF_preview_changed(-1); + allqueue(REDRAWBUTSSHADING, 1); + allqueue(REDRAWIPO, 0); + } +#endif + } + } +} + +void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) +{ + /* XXX This does not work due to layout functions relying on node->block, + * which only exists during actual drawing. Can we rely on valid totr rects? + */ + /* make sure nodes have correct bounding boxes after transform */ + // node_update_nodetree(C, ntree, 0.0f, 0.0f); +} + +/* ***************** generic operator functions for nodes ***************** */ + +#if 0 /* UNUSED */ + +static bool edit_node_poll(bContext *C) +{ + return ED_operator_node_active(C); +} + +static void edit_node_properties(wmOperatorType *ot) +{ + /* XXX could node be a context pointer? */ + RNA_def_string(ot->srna, "node", nullptr, MAX_NAME, "Node", ""); + RNA_def_int(ot->srna, "socket", 0, 0, MAX_SOCKET, "Socket", "", 0, MAX_SOCKET); + RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Side", ""); +} + +static int edit_node_invoke_properties(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "node")) { + bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_Node).data; + if (!node) { + return 0; + } + else { + RNA_string_set(op->ptr, "node", node->name); + } + } + + if (!RNA_struct_property_is_set(op->ptr, "in_out")) { + RNA_enum_set(op->ptr, "in_out", SOCK_IN); + } + + if (!RNA_struct_property_is_set(op->ptr, "socket")) { + RNA_int_set(op->ptr, "socket", 0); + } + + return 1; +} + +static void edit_node_properties_get( + wmOperator *op, bNodeTree *ntree, bNode **r_node, bNodeSocket **r_sock, int *r_in_out) +{ + bNode *node; + bNodeSocket *sock = nullptr; + char nodename[MAX_NAME]; + int sockindex; + int in_out; + + RNA_string_get(op->ptr, "node", nodename); + node = nodeFindNodebyName(ntree, nodename); + + in_out = RNA_enum_get(op->ptr, "in_out"); + + sockindex = RNA_int_get(op->ptr, "socket"); + switch (in_out) { + case SOCK_IN: + sock = BLI_findlink(&node->inputs, sockindex); + break; + case SOCK_OUT: + sock = BLI_findlink(&node->outputs, sockindex); + break; + } + + if (r_node) { + *r_node = node; + } + if (r_sock) { + *r_sock = sock; + } + if (r_in_out) { + *r_in_out = in_out; + } +} +#endif + +/* ************************** Node generic ************** */ + +/* is rct in visible part of node? */ +static bNode *visible_node(SpaceNode *snode, const rctf *rct) +{ + LISTBASE_FOREACH_BACKWARD (bNode *, node, &snode->edittree->nodes) { + if (BLI_rctf_isect(&node->totr, rct, nullptr)) { + return node; + } + } + return nullptr; +} + +/* ********************** size widget operator ******************** */ + +struct NodeSizeWidget { + float mxstart, mystart; + float oldlocx, oldlocy; + float oldoffsetx, oldoffsety; + float oldwidth, oldheight; + int directions; +}; + +static void node_resize_init( + bContext *C, wmOperator *op, const wmEvent *UNUSED(event), bNode *node, int dir) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + NodeSizeWidget *nsw = (NodeSizeWidget *)MEM_callocN(sizeof(NodeSizeWidget), + "size widget op data"); + + op->customdata = nsw; + nsw->mxstart = snode->runtime->cursor[0] * UI_DPI_FAC; + nsw->mystart = snode->runtime->cursor[1] * UI_DPI_FAC; + + /* store old */ + nsw->oldlocx = node->locx; + nsw->oldlocy = node->locy; + nsw->oldoffsetx = node->offsetx; + nsw->oldoffsety = node->offsety; + nsw->oldwidth = node->width; + nsw->oldheight = node->height; + nsw->directions = dir; + + WM_cursor_modal_set(CTX_wm_window(C), node_get_resize_cursor(dir)); + /* add modal handler */ + WM_event_add_modal_handler(C, op); +} + +static void node_resize_exit(bContext *C, wmOperator *op, bool cancel) +{ + WM_cursor_modal_restore(CTX_wm_window(C)); + + /* Restore old data on cancel. */ + if (cancel) { + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node = nodeGetActive(snode->edittree); + NodeSizeWidget *nsw = (NodeSizeWidget *)op->customdata; + + node->locx = nsw->oldlocx; + node->locy = nsw->oldlocy; + node->offsetx = nsw->oldoffsetx; + node->offsety = nsw->oldoffsety; + node->width = nsw->oldwidth; + node->height = nsw->oldheight; + } + + MEM_freeN(op->customdata); + op->customdata = nullptr; +} + +static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + bNode *node = nodeGetActive(snode->edittree); + NodeSizeWidget *nsw = (NodeSizeWidget *)op->customdata; + + switch (event->type) { + case MOUSEMOVE: { + float mx, my; + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &mx, &my); + float dx = (mx - nsw->mxstart) / UI_DPI_FAC; + float dy = (my - nsw->mystart) / UI_DPI_FAC; + + if (node) { + float *pwidth = &node->width; + float oldwidth = nsw->oldwidth; + float widthmin = node->typeinfo->minwidth; + float widthmax = node->typeinfo->maxwidth; + + { + if (nsw->directions & NODE_RESIZE_RIGHT) { + *pwidth = oldwidth + dx; + CLAMP(*pwidth, widthmin, widthmax); + } + if (nsw->directions & NODE_RESIZE_LEFT) { + float locmax = nsw->oldlocx + oldwidth; + + node->locx = nsw->oldlocx + dx; + CLAMP(node->locx, locmax - widthmax, locmax - widthmin); + *pwidth = locmax - node->locx; + } + } + + /* height works the other way round ... */ + { + float heightmin = UI_DPI_FAC * node->typeinfo->minheight; + float heightmax = UI_DPI_FAC * node->typeinfo->maxheight; + if (nsw->directions & NODE_RESIZE_TOP) { + float locmin = nsw->oldlocy - nsw->oldheight; + + node->locy = nsw->oldlocy + dy; + CLAMP(node->locy, locmin + heightmin, locmin + heightmax); + node->height = node->locy - locmin; + } + if (nsw->directions & NODE_RESIZE_BOTTOM) { + node->height = nsw->oldheight - dy; + CLAMP(node->height, heightmin, heightmax); + } + } + + /* XXX make callback? */ + if (node->type == NODE_FRAME) { + /* keep the offset symmetric around center point */ + if (nsw->directions & NODE_RESIZE_LEFT) { + node->locx = nsw->oldlocx + 0.5f * dx; + node->offsetx = nsw->oldoffsetx + 0.5f * dx; + } + if (nsw->directions & NODE_RESIZE_RIGHT) { + node->locx = nsw->oldlocx + 0.5f * dx; + node->offsetx = nsw->oldoffsetx - 0.5f * dx; + } + if (nsw->directions & NODE_RESIZE_TOP) { + node->locy = nsw->oldlocy + 0.5f * dy; + node->offsety = nsw->oldoffsety + 0.5f * dy; + } + if (nsw->directions & NODE_RESIZE_BOTTOM) { + node->locy = nsw->oldlocy + 0.5f * dy; + node->offsety = nsw->oldoffsety - 0.5f * dy; + } + } + } + + ED_region_tag_redraw(region); + + break; + } + case LEFTMOUSE: + case MIDDLEMOUSE: + case RIGHTMOUSE: { + if (event->val == KM_RELEASE) { + node_resize_exit(C, op, false); + ED_node_post_apply_transform(C, snode->edittree); + + return OPERATOR_FINISHED; + } + if (event->val == KM_PRESS) { + node_resize_exit(C, op, true); + ED_region_tag_redraw(region); + + return OPERATOR_CANCELLED; + } + break; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +static int node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + bNode *node = nodeGetActive(snode->edittree); + int dir; + + if (node) { + float cursor[2]; + + /* convert mouse coordinates to v2d space */ + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); + dir = node->typeinfo->resize_area_func(node, cursor[0], cursor[1]); + if (dir != 0) { + node_resize_init(C, op, event, node, dir); + return OPERATOR_RUNNING_MODAL; + } + } + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; +} + +static void node_resize_cancel(bContext *C, wmOperator *op) +{ + node_resize_exit(C, op, true); +} + +void NODE_OT_resize(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Resize Node"; + ot->idname = "NODE_OT_resize"; + ot->description = "Resize a node"; + + /* api callbacks */ + ot->invoke = node_resize_invoke; + ot->modal = node_resize_modal; + ot->poll = ED_operator_node_active; + ot->cancel = node_resize_cancel; + + /* flags */ + ot->flag = OPTYPE_BLOCKING; +} + +/* ********************** hidden sockets ******************** */ + +bool node_has_hidden_sockets(bNode *node) +{ + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (sock->flag & SOCK_HIDDEN) { + return true; + } + } + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + if (sock->flag & SOCK_HIDDEN) { + return true; + } + } + return false; +} + +void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set) +{ + if (set == 0) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + sock->flag &= ~SOCK_HIDDEN; + } + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + sock->flag &= ~SOCK_HIDDEN; + } + } + else { + /* hide unused sockets */ + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (sock->link == nullptr) { + sock->flag |= SOCK_HIDDEN; + } + } + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + if (nodeCountSocketLinks(snode->edittree, sock) == 0) { + sock->flag |= SOCK_HIDDEN; + } + } + } +} + +/* checks snode->mouse position, and returns found node/socket */ +static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSocket *socket) +{ + const float node_socket_height = node_socket_calculate_height(socket); + rctf multi_socket_rect; + /*.xmax = socket->locx + NODE_SOCKSIZE * 5.5f + * would be the same behavior as for regular sockets. + * But keep it smaller because for multi-input socket you + * sometimes want to drag the link to the other side, if you may + * accidentally pick the wrong link otherwise. */ + BLI_rctf_init(&multi_socket_rect, + socket->locx - NODE_SOCKSIZE * 4.0f, + socket->locx + NODE_SOCKSIZE * 2.0f, + socket->locy - node_socket_height, + socket->locy + node_socket_height); + if (BLI_rctf_isect_pt(&multi_socket_rect, cursor[0], cursor[1])) { + return true; + } + return false; +} + +/* type is SOCK_IN and/or SOCK_OUT */ +int node_find_indicated_socket( + SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, const float cursor[2], int in_out) +{ + rctf rect; + + *nodep = nullptr; + *sockp = nullptr; + + /* check if we click in a socket */ + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + BLI_rctf_init_pt_radius(&rect, cursor, NODE_SOCKSIZE + 4); + + if (!(node->flag & NODE_HIDDEN)) { + /* extra padding inside and out - allow dragging on the text areas too */ + if (in_out == SOCK_IN) { + rect.xmax += NODE_SOCKSIZE; + rect.xmin -= NODE_SOCKSIZE * 4; + } + else if (in_out == SOCK_OUT) { + rect.xmax += NODE_SOCKSIZE * 4; + rect.xmin -= NODE_SOCKSIZE; + } + } + + if (in_out & SOCK_IN) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (!nodeSocketIsHidden(sock)) { + if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) { + if (cursor_isect_multi_input_socket(cursor, sock)) { + if (node == visible_node(snode, &rect)) { + *nodep = node; + *sockp = sock; + return 1; + } + } + } + else if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { + if (node == visible_node(snode, &rect)) { + *nodep = node; + *sockp = sock; + return 1; + } + } + } + } + } + if (in_out & SOCK_OUT) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + if (!nodeSocketIsHidden(sock)) { + if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { + if (node == visible_node(snode, &rect)) { + *nodep = node; + *sockp = sock; + return 1; + } + } + } + } + } + } + + return 0; +} + +/* ****************** Duplicate *********************** */ + +static void node_duplicate_reparent_recursive(bNode *node) +{ + bNode *parent; + + node->flag |= NODE_TEST; + + /* find first selected parent */ + for (parent = node->parent; parent; parent = parent->parent) { + if (parent->flag & SELECT) { + if (!(parent->flag & NODE_TEST)) { + node_duplicate_reparent_recursive(parent); + } + break; + } + } + /* reparent node copy to parent copy */ + if (parent) { + nodeDetachNode(node->new_node); + nodeAttachNode(node->new_node, parent->new_node); + } +} + +static int node_duplicate_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs"); + bool do_tag_update = false; + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + bNode *lastnode = (bNode *)ntree->nodes.last; + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->flag & SELECT) { + BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); + + /* To ensure redraws or re-renders happen. */ + ED_node_tag_update_id(snode->id); + } + + /* make sure we don't copy new nodes again! */ + if (node == lastnode) { + break; + } + } + + /* copy links between selected nodes + * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! + */ + bNodeLink *lastlink = (bNodeLink *)ntree->links.last; + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + /* This creates new links between copied nodes. + * If keep_inputs is set, also copies input links from unselected (when fromnode==nullptr)! + */ + if (link->tonode && (link->tonode->flag & NODE_SELECT) && + (keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT)))) { + bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink"); + newlink->flag = link->flag; + newlink->tonode = link->tonode->new_node; + newlink->tosock = link->tosock->new_sock; + if (link->fromnode && (link->fromnode->flag & NODE_SELECT)) { + newlink->fromnode = link->fromnode->new_node; + newlink->fromsock = link->fromsock->new_sock; + } + else { + /* input node not copied, this keeps the original input linked */ + newlink->fromnode = link->fromnode; + newlink->fromsock = link->fromsock; + } + + BLI_addtail(&ntree->links, newlink); + } + + /* make sure we don't copy new links again! */ + if (link == lastlink) { + break; + } + } + + /* clear flags for recursive depth-first iteration */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + node->flag &= ~NODE_TEST; + } + /* reparent copied nodes */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if ((node->flag & SELECT) && !(node->flag & NODE_TEST)) { + node_duplicate_reparent_recursive(node); + } + + /* only has to check old nodes */ + if (node == lastnode) { + break; + } + } + + /* deselect old nodes, select the copies instead */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->flag & SELECT) { + /* has been set during copy above */ + bNode *newnode = node->new_node; + + nodeSetSelected(node, false); + node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE); + nodeSetSelected(newnode, true); + newnode->flag &= ~NODE_ACTIVE_PREVIEW; + + do_tag_update |= (do_tag_update || node_connected_to_output(bmain, ntree, newnode)); + } + + /* make sure we don't copy new nodes again! */ + if (node == lastnode) { + break; + } + } + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + + snode_notify(C, snode); + if (do_tag_update) { + snode_dag_update(C, snode); + } + + return OPERATOR_FINISHED; +} + +void NODE_OT_duplicate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Nodes"; + ot->description = "Duplicate selected nodes"; + ot->idname = "NODE_OT_duplicate"; + + /* api callbacks */ + ot->exec = node_duplicate_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, "keep_inputs", false, "Keep Inputs", "Keep the input links to duplicated nodes"); +} + +bool ED_node_select_check(ListBase *lb) +{ + LISTBASE_FOREACH (bNode *, node, lb) { + if (node->flag & NODE_SELECT) { + return true; + } + } + + return false; +} + +void ED_node_select_all(ListBase *lb, int action) +{ + if (action == SEL_TOGGLE) { + if (ED_node_select_check(lb)) { + action = SEL_DESELECT; + } + else { + action = SEL_SELECT; + } + } + + LISTBASE_FOREACH (bNode *, node, lb) { + switch (action) { + case SEL_SELECT: + nodeSetSelected(node, true); + break; + case SEL_DESELECT: + nodeSetSelected(node, false); + break; + case SEL_INVERT: + nodeSetSelected(node, !(node->flag & SELECT)); + break; + } + } +} + +/* ******************************** */ +/* XXX some code needing updating to operators. */ + +/* goes over all scenes, reads render layers */ +static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + Scene *curscene = CTX_data_scene(C); + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + /* first tag scenes unread */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->id.tag |= LIB_TAG_DOIT; + } + + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + if ((node->type == CMP_NODE_R_LAYERS) || + (node->type == CMP_NODE_CRYPTOMATTE && node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER)) { + ID *id = node->id; + if (id == nullptr) { + continue; + } + if (id->tag & LIB_TAG_DOIT) { + RE_ReadRenderResult(curscene, (Scene *)id); + ntreeCompositTagRender((Scene *)id); + id->tag &= ~LIB_TAG_DOIT; + } + } + } + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_read_viewlayers(wmOperatorType *ot) +{ + + ot->name = "Read View Layers"; + ot->idname = "NODE_OT_read_viewlayers"; + ot->description = "Read all render layers of all used scenes"; + + ot->exec = node_read_viewlayers_exec; + + ot->poll = composite_node_active; + + /* flags */ + ot->flag = 0; +} + +int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *sce = CTX_data_scene(C); + + /* This is actually a test whether scene is used by the compositor or not. + * All the nodes are using same render result, so there is no need to do + * anything smart about check how exactly scene is used. */ + bNode *node = nullptr; + LISTBASE_FOREACH (bNode *, node_iter, &sce->nodetree->nodes) { + if (node_iter->id == (ID *)sce) { + node = node_iter; + break; + } + } + + if (node) { + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&sce->view_layers, node->custom1); + + if (view_layer) { + PointerRNA op_ptr; + + WM_operator_properties_create(&op_ptr, "RENDER_OT_render"); + RNA_string_set(&op_ptr, "layer", view_layer->name); + RNA_string_set(&op_ptr, "scene", sce->id.name + 2); + + /* to keep keypositions */ + sce->r.scemode |= R_NO_FRAME_UPDATE; + + WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr); + + WM_operator_properties_free(&op_ptr); + + return OPERATOR_FINISHED; + } + } + return OPERATOR_CANCELLED; +} + +void NODE_OT_render_changed(wmOperatorType *ot) +{ + ot->name = "Render Changed Layer"; + ot->idname = "NODE_OT_render_changed"; + ot->description = "Render current scene, when input node's layer has been changed"; + + ot->exec = node_render_changed_exec; + + ot->poll = composite_node_active; + + /* flags */ + ot->flag = 0; +} + +/* ****************** Hide operator *********************** */ + +static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag) +{ + int tot_eq = 0, tot_neq = 0; + + /* Toggles the flag on all selected nodes. + * If the flag is set on all nodes it is unset. + * If the flag is not set on all nodes, it is set. + */ + + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + if (node->flag & SELECT) { + + if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) { + continue; + } + if (toggle_flag == NODE_OPTIONS && + !(node->typeinfo->draw_buttons || node->typeinfo->draw_buttons_ex)) { + continue; + } + + if (node->flag & toggle_flag) { + tot_eq++; + } + else { + tot_neq++; + } + } + } + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + if (node->flag & SELECT) { + + if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) { + continue; + } + if (toggle_flag == NODE_OPTIONS && + !(node->typeinfo->draw_buttons || node->typeinfo->draw_buttons_ex)) { + continue; + } + + if ((tot_eq && tot_neq) || tot_eq == 0) { + node->flag |= toggle_flag; + } + else { + node->flag &= ~toggle_flag; + } + } + } +} + +static int node_hide_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + /* sanity checking (poll callback checks this already) */ + if ((snode == nullptr) || (snode->edittree == nullptr)) { + return OPERATOR_CANCELLED; + } + + node_flag_toggle_exec(snode, NODE_HIDDEN); + + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_hide_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide"; + ot->description = "Toggle hiding of selected nodes"; + ot->idname = "NODE_OT_hide_toggle"; + + /* callbacks */ + ot->exec = node_hide_toggle_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + /* sanity checking (poll callback checks this already) */ + if ((snode == nullptr) || (snode->edittree == nullptr)) { + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + node_flag_toggle_exec(snode, NODE_PREVIEW); + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_preview_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Node Preview"; + ot->description = "Toggle preview display for selected nodes"; + ot->idname = "NODE_OT_preview_toggle"; + + /* callbacks */ + ot->exec = node_preview_toggle_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + /* sanity checking (poll callback checks this already) */ + if ((snode == nullptr) || (snode->edittree == nullptr)) { + return OPERATOR_CANCELLED; + } + + node_flag_toggle_exec(snode, NODE_OPTIONS); + + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_options_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Node Options"; + ot->description = "Toggle option buttons display for selected nodes"; + ot->idname = "NODE_OT_options_toggle"; + + /* callbacks */ + ot->exec = node_options_toggle_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + /* sanity checking (poll callback checks this already) */ + if ((snode == nullptr) || (snode->edittree == nullptr)) { + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + /* Toggle for all selected nodes */ + bool hidden = false; + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + if (node->flag & SELECT) { + if (node_has_hidden_sockets(node)) { + hidden = true; + break; + } + } + } + + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + if (node->flag & SELECT) { + node_set_hidden_sockets(snode, node, !hidden); + } + } + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_hide_socket_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Hidden Node Sockets"; + ot->description = "Toggle unused node socket display"; + ot->idname = "NODE_OT_hide_socket_toggle"; + + /* callbacks */ + ot->exec = node_socket_toggle_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Mute operator *********************** */ + +static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bool do_tag_update = false; + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + /* Only allow muting of nodes having a mute func! */ + if ((node->flag & SELECT) && node->typeinfo->update_internal_links) { + node->flag ^= NODE_MUTED; + snode_update(snode, node); + do_tag_update |= (do_tag_update || node_connected_to_output(bmain, snode->edittree, node)); + } + } + + snode_notify(C, snode); + if (do_tag_update) { + snode_dag_update(C, snode); + } + + return OPERATOR_FINISHED; +} + +void NODE_OT_mute_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Node Mute"; + ot->description = "Toggle muting of the nodes"; + ot->idname = "NODE_OT_mute_toggle"; + + /* callbacks */ + ot->exec = node_mute_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Delete operator ******************* */ + +static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bool do_tag_update = false; + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { + if (node->flag & SELECT) { + do_tag_update |= (do_tag_update || node_connected_to_output(bmain, snode->edittree, node)); + nodeRemoveNode(bmain, snode->edittree, node, true); + } + } + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + + snode_notify(C, snode); + if (do_tag_update) { + snode_dag_update(C, snode); + } + + return OPERATOR_FINISHED; +} + +void NODE_OT_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete"; + ot->description = "Delete selected nodes"; + ot->idname = "NODE_OT_delete"; + + /* api callbacks */ + ot->exec = node_delete_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Switch View ******************* */ + +static bool node_switch_view_poll(bContext *C) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + if (snode && snode->edittree) { + return true; + } + + return false; +} + +static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { + if (node->flag & SELECT) { + /* call the update function from the Switch View node */ + node->update = NODE_UPDATE_OPERATOR; + } + } + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_switch_view_update(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Update Views"; + ot->description = "Update views of selected node"; + ot->idname = "NODE_OT_switch_view_update"; + + /* api callbacks */ + ot->exec = node_switch_view_exec; + ot->poll = node_switch_view_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Delete with reconnect ******************* */ +static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { + if (node->flag & SELECT) { + nodeInternalRelink(snode->edittree, node); + nodeRemoveNode(bmain, snode->edittree, node, true); + } + } + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_delete_reconnect(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete with Reconnect"; + ot->description = "Delete nodes; will reconnect nodes as if deletion was muted"; + ot->idname = "NODE_OT_delete_reconnect"; + + /* api callbacks */ + ot->exec = node_delete_reconnect_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** File Output Add Socket ******************* */ + +static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + SpaceNode *snode = CTX_wm_space_node(C); + PointerRNA ptr = CTX_data_pointer_get(C, "node"); + bNodeTree *ntree = nullptr; + bNode *node = nullptr; + char file_path[MAX_NAME]; + + if (ptr.data) { + node = (bNode *)ptr.data; + ntree = (bNodeTree *)ptr.owner_id; + } + else if (snode && snode->edittree) { + ntree = snode->edittree; + node = nodeGetActive(snode->edittree); + } + + if (!node || node->type != CMP_NODE_OUTPUT_FILE) { + return OPERATOR_CANCELLED; + } + + RNA_string_get(op->ptr, "file_path", file_path); + ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format); + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_output_file_add_socket(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add File Node Socket"; + ot->description = "Add a new input to a file output node"; + ot->idname = "NODE_OT_output_file_add_socket"; + + /* callbacks */ + ot->exec = node_output_file_add_socket_exec; + ot->poll = composite_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_string( + ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Subpath of the output file"); +} + +/* ****************** Multi File Output Remove Socket ******************* */ + +static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + PointerRNA ptr = CTX_data_pointer_get(C, "node"); + bNodeTree *ntree = nullptr; + bNode *node = nullptr; + + if (ptr.data) { + node = (bNode *)ptr.data; + ntree = (bNodeTree *)ptr.owner_id; + } + else if (snode && snode->edittree) { + ntree = snode->edittree; + node = nodeGetActive(snode->edittree); + } + + if (!node || node->type != CMP_NODE_OUTPUT_FILE) { + return OPERATOR_CANCELLED; + } + + if (!ntreeCompositOutputFileRemoveActiveSocket(ntree, node)) { + return OPERATOR_CANCELLED; + } + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove File Node Socket"; + ot->description = "Remove active input from a file output node"; + ot->idname = "NODE_OT_output_file_remove_active_socket"; + + /* callbacks */ + ot->exec = node_output_file_remove_active_socket_exec; + ot->poll = composite_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Multi File Output Move Socket ******************* */ + +static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + PointerRNA ptr = CTX_data_pointer_get(C, "node"); + bNode *node = nullptr; + + if (ptr.data) { + node = (bNode *)ptr.data; + } + else if (snode && snode->edittree) { + node = nodeGetActive(snode->edittree); + } + + if (!node || node->type != CMP_NODE_OUTPUT_FILE) { + return OPERATOR_CANCELLED; + } + + NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; + + bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node->inputs, nimf->active_input); + if (!sock) { + return OPERATOR_CANCELLED; + } + + int direction = RNA_enum_get(op->ptr, "direction"); + + if (direction == 1) { + bNodeSocket *before = sock->prev; + if (!before) { + return OPERATOR_CANCELLED; + } + BLI_remlink(&node->inputs, sock); + BLI_insertlinkbefore(&node->inputs, before, sock); + nimf->active_input--; + } + else { + bNodeSocket *after = sock->next; + if (!after) { + return OPERATOR_CANCELLED; + } + BLI_remlink(&node->inputs, sock); + BLI_insertlinkafter(&node->inputs, after, sock); + nimf->active_input++; + } + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_output_file_move_active_socket(wmOperatorType *ot) +{ + static const EnumPropertyItem direction_items[] = { + {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, {0, nullptr, 0, nullptr, nullptr}}; + + /* identifiers */ + ot->name = "Move File Node Socket"; + ot->description = "Move the active input of a file output node up or down the list"; + ot->idname = "NODE_OT_output_file_move_active_socket"; + + /* callbacks */ + ot->exec = node_output_file_move_active_socket_exec; + ot->poll = composite_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", ""); +} + +/* ****************** Copy Node Color ******************* */ + +static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + + if (!ntree) { + return OPERATOR_CANCELLED; + } + bNode *node = nodeGetActive(ntree); + if (!node) { + return OPERATOR_CANCELLED; + } + + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (node_iter->flag & NODE_SELECT && node_iter != node) { + if (node->flag & NODE_CUSTOM_COLOR) { + node_iter->flag |= NODE_CUSTOM_COLOR; + copy_v3_v3(node_iter->color, node->color); + } + else { + node_iter->flag &= ~NODE_CUSTOM_COLOR; + } + } + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_node_copy_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Color"; + ot->description = "Copy color to all selected nodes"; + ot->idname = "NODE_OT_node_copy_color"; + + /* api callbacks */ + ot->exec = node_copy_color_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Copy to clipboard ******************* */ + +static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + /* clear current clipboard */ + BKE_node_clipboard_clear(); + BKE_node_clipboard_init(ntree); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->flag & SELECT) { + /* No ID refcounting, this node is virtual, + * detached from any actual Blender data currently. */ + bNode *new_node = BKE_node_copy_store_new_pointers( + nullptr, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN); + BKE_node_clipboard_add_node(new_node); + } + } + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->flag & SELECT) { + bNode *new_node = node->new_node; + + /* ensure valid pointers */ + if (new_node->parent) { + /* parent pointer must be redirected to new node or detached if parent is + * not copied */ + if (new_node->parent->flag & NODE_SELECT) { + new_node->parent = new_node->parent->new_node; + } + else { + nodeDetachNode(new_node); + } + } + } + } + + /* copy links between selected nodes + * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! + */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + /* This creates new links between copied nodes. */ + if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode && + (link->fromnode->flag & NODE_SELECT)) { + bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink"); + newlink->flag = link->flag; + newlink->tonode = link->tonode->new_node; + newlink->tosock = link->tosock->new_sock; + newlink->fromnode = link->fromnode->new_node; + newlink->fromsock = link->fromsock->new_sock; + + BKE_node_clipboard_add_link(newlink); + } + } + + return OPERATOR_FINISHED; +} + +void NODE_OT_clipboard_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy to Clipboard"; + ot->description = "Copies selected nodes to the clipboard"; + ot->idname = "NODE_OT_clipboard_copy"; + + /* api callbacks */ + ot->exec = node_clipboard_copy_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Paste from clipboard ******************* */ + +static int node_clipboard_paste_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + + /* validate pointers in the clipboard */ + bool is_clipboard_valid = BKE_node_clipboard_validate(); + const ListBase *clipboard_nodes_lb = BKE_node_clipboard_get_nodes(); + const ListBase *clipboard_links_lb = BKE_node_clipboard_get_links(); + + if (BLI_listbase_is_empty(clipboard_nodes_lb)) { + BKE_report(op->reports, RPT_ERROR, "Clipboard is empty"); + return OPERATOR_CANCELLED; + } + + if (BKE_node_clipboard_get_type() != ntree->type) { + BKE_report(op->reports, RPT_ERROR, "Clipboard nodes are an incompatible type"); + return OPERATOR_CANCELLED; + } + + /* only warn */ + if (is_clipboard_valid == false) { + BKE_report(op->reports, + RPT_WARNING, + "Some nodes references could not be restored, will be left empty"); + } + + /* make sure all clipboard nodes would be valid in the target tree */ + bool all_nodes_valid = true; + LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { + const char *disabled_hint = nullptr; + if (!node->typeinfo->poll_instance || + !node->typeinfo->poll_instance(node, ntree, &disabled_hint)) { + all_nodes_valid = false; + if (disabled_hint) { + BKE_reportf(op->reports, + RPT_ERROR, + "Cannot add node %s into node tree %s:\n %s", + node->name, + ntree->id.name + 2, + disabled_hint); + } + else { + BKE_reportf(op->reports, + RPT_ERROR, + "Cannot add node %s into node tree %s", + node->name, + ntree->id.name + 2); + } + } + } + if (!all_nodes_valid) { + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + /* deselect old nodes */ + node_deselect_all(snode); + + /* calculate "barycenter" for placing on mouse cursor */ + float center[2] = {0.0f, 0.0f}; + int num_nodes = 0; + LISTBASE_FOREACH_INDEX (bNode *, node, clipboard_nodes_lb, num_nodes) { + center[0] += BLI_rctf_cent_x(&node->totr); + center[1] += BLI_rctf_cent_y(&node->totr); + } + mul_v2_fl(center, 1.0 / num_nodes); + + /* copy nodes from clipboard */ + LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { + bNode *new_node = BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); + + /* pasted nodes are selected */ + nodeSetSelected(new_node, true); + } + + /* reparent copied nodes */ + LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { + bNode *new_node = node->new_node; + if (new_node->parent) { + new_node->parent = new_node->parent->new_node; + } + } + + LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) { + nodeAddLink(ntree, + link->fromnode->new_node, + link->fromsock->new_sock, + link->tonode->new_node, + link->tosock->new_sock); + } + + Main *bmain = CTX_data_main(C); + ntreeUpdateTree(bmain, snode->edittree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + /* Pasting nodes can create arbitrary new relations, because nodes can reference IDs. */ + DEG_relations_tag_update(bmain); + + return OPERATOR_FINISHED; +} + +void NODE_OT_clipboard_paste(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Paste from Clipboard"; + ot->description = "Pastes nodes from the clipboard to the active node tree"; + ot->idname = "NODE_OT_clipboard_paste"; + + /* api callbacks */ + ot->exec = node_clipboard_paste_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/********************** Add interface socket operator *********************/ + +static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb) +{ + LISTBASE_FOREACH (bNodeSocket *, socket, lb) { + if (socket->flag & SELECT) { + return socket; + } + } + return nullptr; +} + +static int ntree_socket_add_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + + PointerRNA ntree_ptr; + RNA_id_pointer_create((ID *)ntree, &ntree_ptr); + + const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); + ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs; + + const char *default_name = (in_out == SOCK_IN) ? "Input" : "Output"; + bNodeSocket *active_sock = ntree_get_active_interface_socket(sockets); + + bNodeSocket *sock; + if (active_sock) { + /* insert a copy of the active socket right after it */ + sock = ntreeInsertSocketInterface( + ntree, in_out, active_sock->idname, active_sock->next, active_sock->name); + /* XXX this only works for actual sockets, not interface templates! */ + /*nodeSocketCopyValue(sock, &ntree_ptr, active_sock, &ntree_ptr);*/ + } + else { + /* XXX TODO define default socket type for a tree! */ + sock = ntreeAddSocketInterface(ntree, in_out, "NodeSocketFloat", default_name); + } + + /* Deactivate sockets. */ + LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { + socket_iter->flag &= ~SELECT; + } + /* make the new socket active */ + sock->flag |= SELECT; + + ntreeUpdateTree(CTX_data_main(C), ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_tree_socket_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Node Tree Interface Socket"; + ot->description = "Add an input or output socket to the current node tree"; + ot->idname = "NODE_OT_tree_socket_add"; + + /* api callbacks */ + ot->exec = ntree_socket_add_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); +} + +/********************** Remove interface socket operator *********************/ + +static int ntree_socket_remove_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); + + bNodeSocket *iosock = ntree_get_active_interface_socket(in_out == SOCK_IN ? &ntree->inputs : + &ntree->outputs); + if (iosock == nullptr) { + return OPERATOR_CANCELLED; + } + + /* preferably next socket becomes active, otherwise try previous socket */ + bNodeSocket *active_sock = (iosock->next ? iosock->next : iosock->prev); + ntreeRemoveSocketInterface(ntree, iosock); + + /* set active socket */ + if (active_sock) { + active_sock->flag |= SELECT; + } + + ntreeUpdateTree(CTX_data_main(C), ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_tree_socket_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Node Tree Interface Socket"; + ot->description = "Remove an input or output socket to the current node tree"; + ot->idname = "NODE_OT_tree_socket_remove"; + + /* api callbacks */ + ot->exec = ntree_socket_remove_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); +} + +/********************** Move interface socket operator *********************/ + +static const EnumPropertyItem move_direction_items[] = { + {1, "UP", 0, "Up", ""}, + {2, "DOWN", 0, "Down", ""}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static int ntree_socket_move_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + int direction = RNA_enum_get(op->ptr, "direction"); + + const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); + ListBase *sockets = in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs; + + bNodeSocket *iosock = ntree_get_active_interface_socket(sockets); + + if (iosock == nullptr) { + return OPERATOR_CANCELLED; + } + + switch (direction) { + case 1: { /* up */ + bNodeSocket *before = iosock->prev; + BLI_remlink(sockets, iosock); + if (before) { + BLI_insertlinkbefore(sockets, before, iosock); + } + else { + BLI_addhead(sockets, iosock); + } + break; + } + case 2: { /* down */ + bNodeSocket *after = iosock->next; + BLI_remlink(sockets, iosock); + if (after) { + BLI_insertlinkafter(sockets, after, iosock); + } + else { + BLI_addtail(sockets, iosock); + } + break; + } + } + + ntree->update |= NTREE_UPDATE_GROUP; + ntreeUpdateTree(CTX_data_main(C), ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_tree_socket_move(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Move Node Tree Socket"; + ot->description = "Move a socket up or down in the current node tree's sockets stack"; + ot->idname = "NODE_OT_tree_socket_move"; + + /* api callbacks */ + ot->exec = ntree_socket_move_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "direction", move_direction_items, 1, "Direction", ""); + RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); +} + +/* ********************** Shader Script Update ******************/ + +static bool node_shader_script_update_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + const RenderEngineType *type = RE_engines_find(scene->r.engine); + SpaceNode *snode = CTX_wm_space_node(C); + + /* test if we have a render engine that supports shaders scripts */ + if (!(type && type->update_script_node)) { + return false; + } + + /* see if we have a shader script node in context */ + bNode *node = (bNode *)CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data; + + if (!node && snode && snode->edittree) { + node = nodeGetActive(snode->edittree); + } + + if (node && node->type == SH_NODE_SCRIPT) { + NodeShaderScript *nss = (NodeShaderScript *)node->storage; + + if (node->id || nss->filepath[0]) { + return ED_operator_node_editable(C); + } + } + + /* see if we have a text datablock in context */ + Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; + if (text) { + return true; + } + + /* we don't check if text datablock is actually in use, too slow for poll */ + + return false; +} + +/* recursively check for script nodes in groups using this text and update */ +static bool node_shader_script_update_text_recursive(RenderEngine *engine, + RenderEngineType *type, + bNodeTree *ntree, + Text *text) +{ + bool found = false; + + ntree->done = true; + + /* update each script that is using this text datablock */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == NODE_GROUP) { + bNodeTree *ngroup = (bNodeTree *)node->id; + if (ngroup && !ngroup->done) { + found |= node_shader_script_update_text_recursive(engine, type, ngroup, text); + } + } + else if (node->type == SH_NODE_SCRIPT && node->id == &text->id) { + type->update_script_node(engine, ntree, node); + found = true; + } + } + + return found; +} + +static int node_shader_script_update_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + SpaceNode *snode = CTX_wm_space_node(C); + PointerRNA nodeptr = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript); + bool found = false; + + /* setup render engine */ + RenderEngineType *type = RE_engines_find(scene->r.engine); + RenderEngine *engine = RE_engine_create(type); + engine->reports = op->reports; + + /* get node */ + bNodeTree *ntree_base = nullptr; + bNode *node = nullptr; + if (nodeptr.data) { + ntree_base = (bNodeTree *)nodeptr.owner_id; + node = (bNode *)nodeptr.data; + } + else if (snode && snode->edittree) { + ntree_base = snode->edittree; + node = nodeGetActive(snode->edittree); + } + + if (node) { + /* update single node */ + type->update_script_node(engine, ntree_base, node); + + found = true; + } + else { + /* update all nodes using text datablock */ + Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; + + if (text) { + /* clear flags for recursion check */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + ntree->done = false; + } + } + FOREACH_NODETREE_END; + + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + if (!ntree->done) { + found |= node_shader_script_update_text_recursive(engine, type, ntree, text); + } + } + } + FOREACH_NODETREE_END; + + if (!found) { + BKE_report(op->reports, RPT_INFO, "Text not used by any node, no update done"); + } + } + } + + RE_engine_free(engine); + + return (found) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +void NODE_OT_shader_script_update(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Script Node Update"; + ot->description = "Update shader script node with new sockets and options from the script"; + ot->idname = "NODE_OT_shader_script_update"; + + /* api callbacks */ + ot->exec = node_shader_script_update_exec; + ot->poll = node_shader_script_update_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************** Viewer border ******************/ + +static void viewer_border_corner_to_backdrop(SpaceNode *snode, + ARegion *region, + int x, + int y, + int backdrop_width, + int backdrop_height, + float *fx, + float *fy) +{ + float bufx = backdrop_width * snode->zoom; + float bufy = backdrop_height * snode->zoom; + + *fx = (bufx > 0.0f ? ((float)x - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f); + *fy = (bufy > 0.0f ? ((float)y - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f); +} + +static int viewer_border_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + void *lock; + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); + + if (ibuf) { + ARegion *region = CTX_wm_region(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *btree = snode->nodetree; + rcti rect; + rctf rectf; + + /* get border from operator */ + WM_operator_properties_border_to_rcti(op, &rect); + + /* convert border to unified space within backdrop image */ + viewer_border_corner_to_backdrop( + snode, region, rect.xmin, rect.ymin, ibuf->x, ibuf->y, &rectf.xmin, &rectf.ymin); + + viewer_border_corner_to_backdrop( + snode, region, rect.xmax, rect.ymax, ibuf->x, ibuf->y, &rectf.xmax, &rectf.ymax); + + /* clamp coordinates */ + rectf.xmin = max_ff(rectf.xmin, 0.0f); + rectf.ymin = max_ff(rectf.ymin, 0.0f); + rectf.xmax = min_ff(rectf.xmax, 1.0f); + rectf.ymax = min_ff(rectf.ymax, 1.0f); + + if (rectf.xmin < rectf.xmax && rectf.ymin < rectf.ymax) { + btree->viewer_border = rectf; + + if (rectf.xmin == 0.0f && rectf.ymin == 0.0f && rectf.xmax == 1.0f && rectf.ymax == 1.0f) { + btree->flag &= ~NTREE_VIEWER_BORDER; + } + else { + btree->flag |= NTREE_VIEWER_BORDER; + } + + snode_notify(C, snode); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + } + else { + btree->flag &= ~NTREE_VIEWER_BORDER; + } + } + + BKE_image_release_ibuf(ima, ibuf, lock); + + return OPERATOR_FINISHED; +} + +void NODE_OT_viewer_border(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Viewer Region"; + ot->description = "Set the boundaries for viewer operations"; + ot->idname = "NODE_OT_viewer_border"; + + /* api callbacks */ + ot->invoke = WM_gesture_box_invoke; + ot->exec = viewer_border_exec; + ot->modal = WM_gesture_box_modal; + ot->cancel = WM_gesture_box_cancel; + ot->poll = composite_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_gesture_box(ot); +} + +static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *btree = snode->nodetree; + + btree->flag &= ~NTREE_VIEWER_BORDER; + snode_notify(C, snode); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_clear_viewer_border(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Viewer Region"; + ot->description = "Clear the boundaries for viewer operations"; + ot->idname = "NODE_OT_clear_viewer_border"; + + /* api callbacks */ + ot->exec = clear_viewer_border_exec; + ot->poll = composite_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Cryptomatte Add Socket ******************* */ + +static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + PointerRNA ptr = CTX_data_pointer_get(C, "node"); + bNodeTree *ntree = nullptr; + bNode *node = nullptr; + + if (ptr.data) { + node = (bNode *)ptr.data; + ntree = (bNodeTree *)ptr.owner_id; + } + else if (snode && snode->edittree) { + ntree = snode->edittree; + node = nodeGetActive(snode->edittree); + } + + if (!node || node->type != CMP_NODE_CRYPTOMATTE_LEGACY) { + return OPERATOR_CANCELLED; + } + + ntreeCompositCryptomatteAddSocket(ntree, node); + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Cryptomatte Socket"; + ot->description = "Add a new input layer to a Cryptomatte node"; + ot->idname = "NODE_OT_cryptomatte_layer_add"; + + /* callbacks */ + ot->exec = node_cryptomatte_add_socket_exec; + ot->poll = composite_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Cryptomatte Remove Socket ******************* */ + +static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + PointerRNA ptr = CTX_data_pointer_get(C, "node"); + bNodeTree *ntree = nullptr; + bNode *node = nullptr; + + if (ptr.data) { + node = (bNode *)ptr.data; + ntree = (bNodeTree *)ptr.owner_id; + } + else if (snode && snode->edittree) { + ntree = snode->edittree; + node = nodeGetActive(snode->edittree); + } + + if (!node || node->type != CMP_NODE_CRYPTOMATTE_LEGACY) { + return OPERATOR_CANCELLED; + } + + if (!ntreeCompositCryptomatteRemoveSocket(ntree, node)) { + return OPERATOR_CANCELLED; + } + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Cryptomatte Socket"; + ot->description = "Remove layer from a Cryptomatte node"; + ot->idname = "NODE_OT_cryptomatte_layer_remove"; + + /* callbacks */ + ot->exec = node_cryptomatte_remove_socket_exec; + ot->poll = composite_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c deleted file mode 100644 index 335e2f93ff3..00000000000 --- a/source/blender/editors/space_node/node_group.c +++ /dev/null @@ -1,1132 +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. - */ - -/** \file - * \ingroup spnode - */ - -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_anim_types.h" -#include "DNA_node_types.h" - -#include "BLI_linklist.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_string.h" - -#include "BLT_translation.h" - -#include "BKE_action.h" -#include "BKE_animsys.h" -#include "BKE_context.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_report.h" - -#include "DEG_depsgraph_build.h" - -#include "ED_node.h" /* own include */ -#include "ED_render.h" -#include "ED_screen.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "UI_resources.h" - -#include "NOD_common.h" -#include "NOD_socket.h" -#include "node_intern.h" /* own include */ - -/* -------------------------------------------------------------------- */ -/** \name Local Utilities - * \{ */ - -static bool node_group_operator_active_poll(bContext *C) -{ - if (ED_operator_node_active(C)) { - SpaceNode *snode = CTX_wm_space_node(C); - - /* Group operators only defined for standard node tree types. - * Disabled otherwise to allow pynodes define their own operators - * with same keymap. - */ - if (STR_ELEM(snode->tree_idname, - "ShaderNodeTree", - "CompositorNodeTree", - "TextureNodeTree", - "GeometryNodeTree")) { - return true; - } - } - return false; -} - -static bool node_group_operator_editable(bContext *C) -{ - if (ED_operator_node_editable(C)) { - SpaceNode *snode = CTX_wm_space_node(C); - - /* Group operators only defined for standard node tree types. - * Disabled otherwise to allow pynodes define their own operators - * with same keymap. - */ - if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) || - ED_node_is_geometry(snode)) { - return true; - } - } - return false; -} - -static const char *group_ntree_idname(bContext *C) -{ - SpaceNode *snode = CTX_wm_space_node(C); - return snode->tree_idname; -} - -const char *node_group_idname(bContext *C) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - if (ED_node_is_shader(snode)) { - return "ShaderNodeGroup"; - } - if (ED_node_is_compositor(snode)) { - return "CompositorNodeGroup"; - } - if (ED_node_is_texture(snode)) { - return "TextureNodeGroup"; - } - if (ED_node_is_geometry(snode)) { - return "GeometryNodeGroup"; - } - - return ""; -} - -static bNode *node_group_get_active(bContext *C, const char *node_idname) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNode *node = nodeGetActive(snode->edittree); - - if (node && STREQ(node->idname, node_idname)) { - return node; - } - return NULL; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Edit Group Operator - * \{ */ - -static int node_group_edit_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - const char *node_idname = node_group_idname(C); - const bool exit = RNA_boolean_get(op->ptr, "exit"); - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - bNode *gnode = node_group_get_active(C, node_idname); - - if (gnode && !exit) { - bNodeTree *ngroup = (bNodeTree *)gnode->id; - - if (ngroup) { - ED_node_tree_push(snode, ngroup, gnode); - } - } - else { - ED_node_tree_pop(snode); - } - - WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_group_edit(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Edit Group"; - ot->description = "Edit node group"; - ot->idname = "NODE_OT_group_edit"; - - /* api callbacks */ - ot->exec = node_group_edit_exec; - ot->poll = node_group_operator_active_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean(ot->srna, "exit", false, "Exit", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Ungroup Operator - * \{ */ - -/** - * The given paths will be owned by the returned instance. - * Both pointers are allowed to point to the same string. - */ -static AnimationBasePathChange *animation_basepath_change_new(const char *src_basepath, - const char *dst_basepath) -{ - AnimationBasePathChange *basepath_change = MEM_callocN(sizeof(*basepath_change), AT); - basepath_change->src_basepath = src_basepath; - basepath_change->dst_basepath = dst_basepath; - return basepath_change; -} - -static void animation_basepath_change_free(AnimationBasePathChange *basepath_change) -{ - if (basepath_change->src_basepath != basepath_change->dst_basepath) { - MEM_freeN((void *)basepath_change->src_basepath); - } - MEM_freeN((void *)basepath_change->dst_basepath); - MEM_freeN(basepath_change); -} - -/* returns 1 if its OK */ -static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) -{ - /* clear new pointers, set in copytree */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - node->new_node = NULL; - } - - ListBase anim_basepaths = {NULL, NULL}; - LinkNode *nodes_delayed_free = NULL; - bNodeTree *ngroup = (bNodeTree *)gnode->id; - - /* wgroup is a temporary copy of the NodeTree we're merging in - * - all of wgroup's nodes are copied across to their new home - * - ngroup (i.e. the source NodeTree) is left unscathed - * - temp copy. do change ID usercount for the copies - */ - bNodeTree *wgroup = ntreeCopyTree_ex_new_pointers(ngroup, bmain, true); - - /* Add the nodes into the ntree */ - LISTBASE_FOREACH_MUTABLE (bNode *, node, &wgroup->nodes) { - /* Remove interface nodes. - * This also removes remaining links to and from interface nodes. - */ - if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) { - /* We must delay removal since sockets will reference this node. see: T52092 */ - BLI_linklist_prepend(&nodes_delayed_free, node); - } - - /* keep track of this node's RNA "base" path (the part of the path identifying the node) - * if the old nodetree has animation data which potentially covers this node - */ - const char *old_animation_basepath = NULL; - if (wgroup->adt) { - PointerRNA ptr; - RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr); - old_animation_basepath = RNA_path_from_ID_to_struct(&ptr); - } - - /* migrate node */ - BLI_remlink(&wgroup->nodes, node); - BLI_addtail(&ntree->nodes, node); - - /* ensure unique node name in the node tree */ - nodeUniqueName(ntree, node); - - if (wgroup->adt) { - PointerRNA ptr; - RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr); - const char *new_animation_basepath = RNA_path_from_ID_to_struct(&ptr); - BLI_addtail(&anim_basepaths, - animation_basepath_change_new(old_animation_basepath, new_animation_basepath)); - } - - if (!node->parent) { - node->locx += gnode->locx; - node->locy += gnode->locy; - } - - node->flag |= NODE_SELECT; - } - - bNodeLink *glinks_first = ntree->links.last; - - /* Add internal links to the ntree */ - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) { - BLI_remlink(&wgroup->links, link); - BLI_addtail(&ntree->links, link); - } - - bNodeLink *glinks_last = ntree->links.last; - - /* and copy across the animation, - * note that the animation data's action can be NULL here */ - if (wgroup->adt) { - bAction *waction; - - /* firstly, wgroup needs to temporary dummy action - * that can be destroyed, as it shares copies */ - waction = wgroup->adt->action = (bAction *)BKE_id_copy(bmain, &wgroup->adt->action->id); - - /* now perform the moving */ - BKE_animdata_transfer_by_basepath(bmain, &wgroup->id, &ntree->id, &anim_basepaths); - - /* paths + their wrappers need to be freed */ - LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) { - animation_basepath_change_free(basepath_change); - } - - /* free temp action too */ - if (waction) { - BKE_id_free(bmain, waction); - wgroup->adt->action = NULL; - } - } - - /* free the group tree (takes care of user count) */ - BKE_id_free(bmain, wgroup); - - /* restore external links to and from the gnode */ - - /* input links */ - if (glinks_first != NULL) { - for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) { - if (link->fromnode->type == NODE_GROUP_INPUT) { - const char *identifier = link->fromsock->identifier; - int num_external_links = 0; - - /* find external links to this input */ - for (bNodeLink *tlink = ntree->links.first; tlink != glinks_first->next; - tlink = tlink->next) { - if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) { - nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock); - num_external_links++; - } - } - - /* if group output is not externally linked, - * convert the constant input value to ensure somewhat consistent behavior */ - if (num_external_links == 0) { - /* TODO */ -#if 0 - bNodeSocket *sock = node_group_find_input_socket(gnode, identifier); - BLI_assert(sock); - - nodeSocketCopy( - ntree, link->tosock->new_sock, link->tonode->new_node, ntree, sock, gnode); -#endif - } - } - } - - /* Also iterate over new links to cover passthrough links. */ - glinks_last = ntree->links.last; - - /* output links */ - for (bNodeLink *link = ntree->links.first; link != glinks_first->next; link = link->next) { - if (link->fromnode == gnode) { - const char *identifier = link->fromsock->identifier; - int num_internal_links = 0; - - /* find internal links to this output */ - for (bNodeLink *tlink = glinks_first->next; tlink != glinks_last->next; - tlink = tlink->next) { - /* only use active output node */ - if (tlink->tonode->type == NODE_GROUP_OUTPUT && (tlink->tonode->flag & NODE_DO_OUTPUT)) { - if (STREQ(tlink->tosock->identifier, identifier)) { - nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock); - num_internal_links++; - } - } - } - - /* if group output is not internally linked, - * convert the constant output value to ensure somewhat consistent behavior */ - if (num_internal_links == 0) { - /* TODO */ -#if 0 - bNodeSocket *sock = node_group_find_output_socket(gnode, identifier); - BLI_assert(sock); - - nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode); -#endif - } - } - } - } - - while (nodes_delayed_free) { - bNode *node = BLI_linklist_pop(&nodes_delayed_free); - nodeRemoveNode(bmain, ntree, node, false); - } - - /* delete the group instance and dereference group tree */ - nodeRemoveNode(bmain, ntree, gnode, true); - - ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; - - return 1; -} - -static int node_group_ungroup_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - const char *node_idname = node_group_idname(C); - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - bNode *gnode = node_group_get_active(C, node_idname); - if (!gnode) { - return OPERATOR_CANCELLED; - } - - if (gnode->id && node_group_ungroup(bmain, snode->edittree, gnode)) { - ntreeUpdateTree(bmain, snode->nodetree); - } - else { - BKE_report(op->reports, RPT_WARNING, "Cannot ungroup"); - return OPERATOR_CANCELLED; - } - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_group_ungroup(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Ungroup"; - ot->description = "Ungroup selected nodes"; - ot->idname = "NODE_OT_group_ungroup"; - - /* api callbacks */ - ot->exec = node_group_ungroup_exec; - ot->poll = node_group_operator_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Separate Operator - * \{ */ - -/* returns 1 if its OK */ -static int node_group_separate_selected( - Main *bmain, bNodeTree *ntree, bNodeTree *ngroup, float offx, float offy, int make_copy) -{ - /* deselect all nodes in the target tree */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - nodeSetSelected(node, false); - } - - /* clear new pointers, set in BKE_node_copy_ex(). */ - LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { - node->new_node = NULL; - } - - ListBase anim_basepaths = {NULL, NULL}; - - /* add selected nodes into the ntree */ - LISTBASE_FOREACH_MUTABLE (bNode *, node, &ngroup->nodes) { - if (!(node->flag & NODE_SELECT)) { - continue; - } - - /* ignore interface nodes */ - if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) { - nodeSetSelected(node, false); - continue; - } - - bNode *newnode; - if (make_copy) { - /* make a copy */ - newnode = BKE_node_copy_store_new_pointers(ngroup, node, LIB_ID_COPY_DEFAULT); - } - else { - /* use the existing node */ - newnode = node; - } - - /* keep track of this node's RNA "base" path (the part of the path identifying the node) - * if the old nodetree has animation data which potentially covers this node - */ - if (ngroup->adt) { - PointerRNA ptr; - char *path; - - RNA_pointer_create(&ngroup->id, &RNA_Node, newnode, &ptr); - path = RNA_path_from_ID_to_struct(&ptr); - - if (path) { - BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path)); - } - } - - /* ensure valid parent pointers, detach if parent stays inside the group */ - if (newnode->parent && !(newnode->parent->flag & NODE_SELECT)) { - nodeDetachNode(newnode); - } - - /* migrate node */ - BLI_remlink(&ngroup->nodes, newnode); - BLI_addtail(&ntree->nodes, newnode); - - /* ensure unique node name in the node tree */ - nodeUniqueName(ntree, newnode); - - if (!newnode->parent) { - newnode->locx += offx; - newnode->locy += offy; - } - } - - /* add internal links to the ntree */ - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup->links) { - const bool fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT)); - const bool toselect = (link->tonode && (link->tonode->flag & NODE_SELECT)); - - if (make_copy) { - /* make a copy of internal links */ - if (fromselect && toselect) { - nodeAddLink(ntree, - link->fromnode->new_node, - link->fromsock->new_sock, - link->tonode->new_node, - link->tosock->new_sock); - } - } - else { - /* move valid links over, delete broken links */ - if (fromselect && toselect) { - BLI_remlink(&ngroup->links, link); - BLI_addtail(&ntree->links, link); - } - else if (fromselect || toselect) { - nodeRemLink(ngroup, link); - } - } - } - - /* and copy across the animation, - * note that the animation data's action can be NULL here */ - if (ngroup->adt) { - /* now perform the moving */ - BKE_animdata_transfer_by_basepath(bmain, &ngroup->id, &ntree->id, &anim_basepaths); - - /* paths + their wrappers need to be freed */ - LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) { - animation_basepath_change_free(basepath_change); - } - } - - ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; - if (!make_copy) { - ngroup->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; - } - - return 1; -} - -typedef enum eNodeGroupSeparateType { - NODE_GS_COPY, - NODE_GS_MOVE, -} eNodeGroupSeparateType; - -/* Operator Property */ -static const EnumPropertyItem node_group_separate_types[] = { - {NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"}, - {NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"}, - {0, NULL, 0, NULL, NULL}, -}; - -static int node_group_separate_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - int type = RNA_enum_get(op->ptr, "type"); - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - /* are we inside of a group? */ - bNodeTree *ngroup = snode->edittree; - bNodeTree *nparent = ED_node_tree_get(snode, 1); - if (!nparent) { - BKE_report(op->reports, RPT_WARNING, "Not inside node group"); - return OPERATOR_CANCELLED; - } - /* get node tree offset */ - float offx, offy; - space_node_group_offset(snode, &offx, &offy); - - switch (type) { - case NODE_GS_COPY: - if (!node_group_separate_selected(bmain, nparent, ngroup, offx, offy, true)) { - BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes"); - return OPERATOR_CANCELLED; - } - break; - case NODE_GS_MOVE: - if (!node_group_separate_selected(bmain, nparent, ngroup, offx, offy, false)) { - BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes"); - return OPERATOR_CANCELLED; - } - break; - } - - /* switch to parent tree */ - ED_node_tree_pop(snode); - - ntreeUpdateTree(CTX_data_main(C), snode->nodetree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -static int node_group_separate_invoke(bContext *C, - wmOperator *UNUSED(op), - const wmEvent *UNUSED(event)) -{ - uiPopupMenu *pup = UI_popup_menu_begin( - C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE); - uiLayout *layout = UI_popup_menu_layout(pup); - - uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); - uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_COPY); - uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_MOVE); - - UI_popup_menu_end(C, pup); - - return OPERATOR_INTERFACE; -} - -void NODE_OT_group_separate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Separate"; - ot->description = "Separate selected nodes from the node group"; - ot->idname = "NODE_OT_group_separate"; - - /* api callbacks */ - ot->invoke = node_group_separate_invoke; - ot->exec = node_group_separate_exec; - ot->poll = node_group_operator_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "type", node_group_separate_types, NODE_GS_COPY, "Type", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Make Group Operator - * \{ */ - -static bool node_group_make_use_node(bNode *node, bNode *gnode) -{ - return (node != gnode && !ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT) && - (node->flag & NODE_SELECT)); -} - -static bool node_group_make_test_selected(bNodeTree *ntree, - bNode *gnode, - const char *ntree_idname, - struct ReportList *reports) -{ - int ok = true; - - /* make a local pseudo node tree to pass to the node poll functions */ - bNodeTree *ngroup = ntreeAddTree(NULL, "Pseudo Node Group", ntree_idname); - - /* check poll functions for selected nodes */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node_group_make_use_node(node, gnode)) { - const char *disabled_hint = NULL; - if (node->typeinfo->poll_instance && - !node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) { - if (disabled_hint) { - BKE_reportf(reports, - RPT_WARNING, - "Can not add node '%s' in a group:\n %s", - node->name, - disabled_hint); - } - else { - BKE_reportf(reports, RPT_WARNING, "Can not add node '%s' in a group", node->name); - } - ok = false; - break; - } - } - - node->done = 0; - } - - /* free local pseudo node tree again */ - ntreeFreeTree(ngroup); - MEM_freeN(ngroup); - if (!ok) { - return false; - } - - /* check if all connections are OK, no unselected node has both - * inputs and outputs to a selection */ - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - if (node_group_make_use_node(link->fromnode, gnode)) { - link->tonode->done |= 1; - } - if (node_group_make_use_node(link->tonode, gnode)) { - link->fromnode->done |= 2; - } - } - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (!(node->flag & NODE_SELECT) && node != gnode && node->done == 3) { - return false; - } - } - return true; -} - -static int node_get_selected_minmax( - bNodeTree *ntree, bNode *gnode, float *min, float *max, bool use_size) -{ - int totselect = 0; - - INIT_MINMAX2(min, max); - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node_group_make_use_node(node, gnode)) { - float loc[2]; - nodeToView(node, node->offsetx, node->offsety, &loc[0], &loc[1]); - minmax_v2v2_v2(min, max, loc); - if (use_size) { - loc[0] += node->width; - loc[1] -= node->height; - minmax_v2v2_v2(min, max, loc); - } - totselect++; - } - } - - /* sane min/max if no selected nodes */ - if (totselect == 0) { - min[0] = min[1] = max[0] = max[1] = 0.0f; - } - - return totselect; -} - -static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, bNode *gnode) -{ - Main *bmain = CTX_data_main(C); - bNodeTree *ngroup = (bNodeTree *)gnode->id; - bool expose_visible = false; - - /* XXX rough guess, not nice but we don't have access to UI constants here ... */ - static const float offsetx = 200; - static const float offsety = 0.0f; - - /* deselect all nodes in the target tree */ - LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { - nodeSetSelected(node, false); - } - - float center[2], min[2], max[2]; - const int totselect = node_get_selected_minmax(ntree, gnode, min, max, false); - add_v2_v2v2(center, min, max); - mul_v2_fl(center, 0.5f); - - float real_min[2], real_max[2]; - node_get_selected_minmax(ntree, gnode, real_min, real_max, true); - - /* auto-add interface for "solo" nodes */ - if (totselect == 1) { - expose_visible = true; - } - - ListBase anim_basepaths = {NULL, NULL}; - - /* move nodes over */ - LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { - if (node_group_make_use_node(node, gnode)) { - /* keep track of this node's RNA "base" path (the part of the pat identifying the node) - * if the old nodetree has animation data which potentially covers this node - */ - if (ntree->adt) { - PointerRNA ptr; - char *path; - - RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr); - path = RNA_path_from_ID_to_struct(&ptr); - - if (path) { - BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path)); - } - } - - /* ensure valid parent pointers, detach if parent stays outside the group */ - if (node->parent && !(node->parent->flag & NODE_SELECT)) { - nodeDetachNode(node); - } - - /* change node-collection membership */ - BLI_remlink(&ntree->nodes, node); - BLI_addtail(&ngroup->nodes, node); - - /* ensure unique node name in the ngroup */ - nodeUniqueName(ngroup, node); - } - } - - /* move animation data over */ - if (ntree->adt) { - BKE_animdata_transfer_by_basepath(bmain, &ntree->id, &ngroup->id, &anim_basepaths); - - /* paths + their wrappers need to be freed */ - LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) { - animation_basepath_change_free(basepath_change); - } - } - - /* node groups don't use internal cached data */ - ntreeFreeCache(ngroup); - - /* create input node */ - bNode *input_node = nodeAddStaticNode(C, ngroup, NODE_GROUP_INPUT); - input_node->locx = real_min[0] - center[0] - offsetx; - input_node->locy = -offsety; - - /* create output node */ - bNode *output_node = nodeAddStaticNode(C, ngroup, NODE_GROUP_OUTPUT); - output_node->locx = real_max[0] - center[0] + offsetx * 0.25f; - output_node->locy = -offsety; - - /* relink external sockets */ - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { - int fromselect = node_group_make_use_node(link->fromnode, gnode); - int toselect = node_group_make_use_node(link->tonode, gnode); - - if ((fromselect && link->tonode == gnode) || (toselect && link->fromnode == gnode)) { - /* remove all links to/from the gnode. - * this can remove link information, but there's no general way to preserve it. - */ - nodeRemLink(ntree, link); - } - else if (toselect && !fromselect) { - bNodeSocket *link_sock; - bNode *link_node; - node_socket_skip_reroutes(&ntree->links, link->tonode, link->tosock, &link_node, &link_sock); - bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link_node, link_sock); - - /* update the group node and interface node sockets, - * so the new interface socket can be linked. - */ - node_group_update(ntree, gnode); - node_group_input_update(ngroup, input_node); - - /* create new internal link */ - bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier); - nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock); - - /* redirect external link */ - link->tonode = gnode; - link->tosock = node_group_find_input_socket(gnode, iosock->identifier); - } - else if (fromselect && !toselect) { - /* First check whether the source of this link is already connected to an output. - * If yes, reuse that output instead of duplicating it. */ - bool connected = false; - LISTBASE_FOREACH (bNodeLink *, olink, &ngroup->links) { - if (olink->fromsock == link->fromsock && olink->tonode == output_node) { - bNodeSocket *output_sock = node_group_find_output_socket(gnode, - olink->tosock->identifier); - link->fromnode = gnode; - link->fromsock = output_sock; - connected = true; - } - } - - if (!connected) { - bNodeSocket *link_sock; - bNode *link_node; - node_socket_skip_reroutes( - &ntree->links, link->fromnode, link->fromsock, &link_node, &link_sock); - bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link_node, link_sock); - - /* update the group node and interface node sockets, - * so the new interface socket can be linked. - */ - node_group_update(ntree, gnode); - node_group_output_update(ngroup, output_node); - - /* create new internal link */ - bNodeSocket *output_sock = node_group_output_find_socket(output_node, iosock->identifier); - nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock); - - /* redirect external link */ - link->fromnode = gnode; - link->fromsock = node_group_find_output_socket(gnode, iosock->identifier); - } - } - } - - /* move internal links */ - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { - int fromselect = node_group_make_use_node(link->fromnode, gnode); - int toselect = node_group_make_use_node(link->tonode, gnode); - - if (fromselect && toselect) { - BLI_remlink(&ntree->links, link); - BLI_addtail(&ngroup->links, link); - } - } - - /* move nodes in the group to the center */ - LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { - if (node_group_make_use_node(node, gnode) && !node->parent) { - node->locx -= center[0]; - node->locy -= center[1]; - } - } - - /* expose all unlinked sockets too but only the visible ones*/ - if (expose_visible) { - LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { - if (node_group_make_use_node(node, gnode)) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - bool skip = false; - LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) { - if (link->tosock == sock) { - skip = true; - break; - } - } - if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) { - skip = true; - } - if (skip) { - continue; - } - - bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock); - - node_group_input_update(ngroup, input_node); - - /* create new internal link */ - bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier); - nodeAddLink(ngroup, input_node, input_sock, node, sock); - } - - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - bool skip = false; - LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) { - if (link->fromsock == sock) { - skip = true; - } - } - if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) { - skip = true; - } - if (skip) { - continue; - } - - bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock); - - node_group_output_update(ngroup, output_node); - - /* create new internal link */ - bNodeSocket *output_sock = node_group_output_find_socket(output_node, - iosock->identifier); - nodeAddLink(ngroup, node, sock, output_node, output_sock); - } - } - } - } - - /* update of the group tree */ - ngroup->update |= NTREE_UPDATE | NTREE_UPDATE_LINKS; - /* update of the tree containing the group instance node */ - ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; -} - -static bNode *node_group_make_from_selected(const bContext *C, - bNodeTree *ntree, - const char *ntype, - const char *ntreetype) -{ - Main *bmain = CTX_data_main(C); - - float min[2], max[2]; - const int totselect = node_get_selected_minmax(ntree, NULL, min, max, false); - /* don't make empty group */ - if (totselect == 0) { - return NULL; - } - - /* new nodetree */ - bNodeTree *ngroup = ntreeAddTree(bmain, "NodeGroup", ntreetype); - - /* make group node */ - bNode *gnode = nodeAddNode(C, ntree, ntype); - gnode->id = (ID *)ngroup; - - gnode->locx = 0.5f * (min[0] + max[0]); - gnode->locy = 0.5f * (min[1] + max[1]); - - node_group_make_insert_selected(C, ntree, gnode); - - /* update of the tree containing the group instance node */ - ntree->update |= NTREE_UPDATE_NODES; - - return gnode; -} - -static int node_group_make_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - const char *ntree_idname = group_ntree_idname(C); - const char *node_idname = node_group_idname(C); - Main *bmain = CTX_data_main(C); - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - if (!node_group_make_test_selected(ntree, NULL, ntree_idname, op->reports)) { - return OPERATOR_CANCELLED; - } - - bNode *gnode = node_group_make_from_selected(C, ntree, node_idname, ntree_idname); - - if (gnode) { - bNodeTree *ngroup = (bNodeTree *)gnode->id; - - nodeSetActive(ntree, gnode); - if (ngroup) { - ED_node_tree_push(snode, ngroup, gnode); - LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { - sort_multi_input_socket_links(snode, node, NULL, NULL); - } - ntreeUpdateTree(bmain, ngroup); - } - } - - ntreeUpdateTree(bmain, ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - /* We broke relations in node tree, need to rebuild them in the graphs. */ - DEG_relations_tag_update(bmain); - - return OPERATOR_FINISHED; -} - -void NODE_OT_group_make(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Make Group"; - ot->description = "Make group from selected nodes"; - ot->idname = "NODE_OT_group_make"; - - /* api callbacks */ - ot->exec = node_group_make_exec; - ot->poll = node_group_operator_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Group Insert Operator - * \{ */ - -static int node_group_insert_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - const char *node_idname = node_group_idname(C); - Main *bmain = CTX_data_main(C); - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - bNode *gnode = node_group_get_active(C, node_idname); - - if (!gnode || !gnode->id) { - return OPERATOR_CANCELLED; - } - - bNodeTree *ngroup = (bNodeTree *)gnode->id; - if (!node_group_make_test_selected(ntree, gnode, ngroup->idname, op->reports)) { - return OPERATOR_CANCELLED; - } - - node_group_make_insert_selected(C, ntree, gnode); - - nodeSetActive(ntree, gnode); - ED_node_tree_push(snode, ngroup, gnode); - ntreeUpdateTree(bmain, ngroup); - - ntreeUpdateTree(bmain, ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_group_insert(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Group Insert"; - ot->description = "Insert selected nodes into a node group"; - ot->idname = "NODE_OT_group_insert"; - - /* api callbacks */ - ot->exec = node_group_insert_exec; - ot->poll = node_group_operator_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc new file mode 100644 index 00000000000..c4880e1b49a --- /dev/null +++ b/source/blender/editors/space_node/node_group.cc @@ -0,0 +1,1134 @@ +/* + * 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. + */ + +/** \file + * \ingroup spnode + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_anim_types.h" +#include "DNA_node_types.h" + +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "BKE_action.h" +#include "BKE_animsys.h" +#include "BKE_context.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "DEG_depsgraph_build.h" + +#include "ED_node.h" /* own include */ +#include "ED_render.h" +#include "ED_screen.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_resources.h" + +#include "NOD_common.h" +#include "NOD_socket.h" +#include "node_intern.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name Local Utilities + * \{ */ + +static bool node_group_operator_active_poll(bContext *C) +{ + if (ED_operator_node_active(C)) { + SpaceNode *snode = CTX_wm_space_node(C); + + /* Group operators only defined for standard node tree types. + * Disabled otherwise to allow pynodes define their own operators + * with same keymap. + */ + if (STR_ELEM(snode->tree_idname, + "ShaderNodeTree", + "CompositorNodeTree", + "TextureNodeTree", + "GeometryNodeTree")) { + return true; + } + } + return false; +} + +static bool node_group_operator_editable(bContext *C) +{ + if (ED_operator_node_editable(C)) { + SpaceNode *snode = CTX_wm_space_node(C); + + /* Group operators only defined for standard node tree types. + * Disabled otherwise to allow pynodes define their own operators + * with same keymap. + */ + if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) || + ED_node_is_geometry(snode)) { + return true; + } + } + return false; +} + +static const char *group_ntree_idname(bContext *C) +{ + SpaceNode *snode = CTX_wm_space_node(C); + return snode->tree_idname; +} + +const char *node_group_idname(bContext *C) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + if (ED_node_is_shader(snode)) { + return "ShaderNodeGroup"; + } + if (ED_node_is_compositor(snode)) { + return "CompositorNodeGroup"; + } + if (ED_node_is_texture(snode)) { + return "TextureNodeGroup"; + } + if (ED_node_is_geometry(snode)) { + return "GeometryNodeGroup"; + } + + return ""; +} + +static bNode *node_group_get_active(bContext *C, const char *node_idname) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node = nodeGetActive(snode->edittree); + + if (node && STREQ(node->idname, node_idname)) { + return node; + } + return nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edit Group Operator + * \{ */ + +static int node_group_edit_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + const char *node_idname = node_group_idname(C); + const bool exit = RNA_boolean_get(op->ptr, "exit"); + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + bNode *gnode = node_group_get_active(C, node_idname); + + if (gnode && !exit) { + bNodeTree *ngroup = (bNodeTree *)gnode->id; + + if (ngroup) { + ED_node_tree_push(snode, ngroup, gnode); + } + } + else { + ED_node_tree_pop(snode); + } + + WM_event_add_notifier(C, NC_SCENE | ND_NODES, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_group_edit(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Edit Group"; + ot->description = "Edit node group"; + ot->idname = "NODE_OT_group_edit"; + + /* api callbacks */ + ot->exec = node_group_edit_exec; + ot->poll = node_group_operator_active_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "exit", false, "Exit", ""); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Ungroup Operator + * \{ */ + +/** + * The given paths will be owned by the returned instance. + * Both pointers are allowed to point to the same string. + */ +static AnimationBasePathChange *animation_basepath_change_new(const char *src_basepath, + const char *dst_basepath) +{ + AnimationBasePathChange *basepath_change = (AnimationBasePathChange *)MEM_callocN( + sizeof(*basepath_change), AT); + basepath_change->src_basepath = src_basepath; + basepath_change->dst_basepath = dst_basepath; + return basepath_change; +} + +static void animation_basepath_change_free(AnimationBasePathChange *basepath_change) +{ + if (basepath_change->src_basepath != basepath_change->dst_basepath) { + MEM_freeN((void *)basepath_change->src_basepath); + } + MEM_freeN((void *)basepath_change->dst_basepath); + MEM_freeN(basepath_change); +} + +/* returns 1 if its OK */ +static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) +{ + /* clear new pointers, set in copytree */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + node->new_node = nullptr; + } + + ListBase anim_basepaths = {nullptr, nullptr}; + LinkNode *nodes_delayed_free = nullptr; + bNodeTree *ngroup = (bNodeTree *)gnode->id; + + /* wgroup is a temporary copy of the NodeTree we're merging in + * - all of wgroup's nodes are copied across to their new home + * - ngroup (i.e. the source NodeTree) is left unscathed + * - temp copy. do change ID usercount for the copies + */ + bNodeTree *wgroup = ntreeCopyTree_ex_new_pointers(ngroup, bmain, true); + + /* Add the nodes into the ntree */ + LISTBASE_FOREACH_MUTABLE (bNode *, node, &wgroup->nodes) { + /* Remove interface nodes. + * This also removes remaining links to and from interface nodes. + */ + if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) { + /* We must delay removal since sockets will reference this node. see: T52092 */ + BLI_linklist_prepend(&nodes_delayed_free, node); + } + + /* keep track of this node's RNA "base" path (the part of the path identifying the node) + * if the old nodetree has animation data which potentially covers this node + */ + const char *old_animation_basepath = nullptr; + if (wgroup->adt) { + PointerRNA ptr; + RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr); + old_animation_basepath = RNA_path_from_ID_to_struct(&ptr); + } + + /* migrate node */ + BLI_remlink(&wgroup->nodes, node); + BLI_addtail(&ntree->nodes, node); + + /* ensure unique node name in the node tree */ + nodeUniqueName(ntree, node); + + if (wgroup->adt) { + PointerRNA ptr; + RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr); + const char *new_animation_basepath = RNA_path_from_ID_to_struct(&ptr); + BLI_addtail(&anim_basepaths, + animation_basepath_change_new(old_animation_basepath, new_animation_basepath)); + } + + if (!node->parent) { + node->locx += gnode->locx; + node->locy += gnode->locy; + } + + node->flag |= NODE_SELECT; + } + + bNodeLink *glinks_first = (bNodeLink *)ntree->links.last; + + /* Add internal links to the ntree */ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) { + BLI_remlink(&wgroup->links, link); + BLI_addtail(&ntree->links, link); + } + + bNodeLink *glinks_last = (bNodeLink *)ntree->links.last; + + /* and copy across the animation, + * note that the animation data's action can be nullptr here */ + if (wgroup->adt) { + bAction *waction; + + /* firstly, wgroup needs to temporary dummy action + * that can be destroyed, as it shares copies */ + waction = wgroup->adt->action = (bAction *)BKE_id_copy(bmain, &wgroup->adt->action->id); + + /* now perform the moving */ + BKE_animdata_transfer_by_basepath(bmain, &wgroup->id, &ntree->id, &anim_basepaths); + + /* paths + their wrappers need to be freed */ + LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) { + animation_basepath_change_free(basepath_change); + } + + /* free temp action too */ + if (waction) { + BKE_id_free(bmain, waction); + wgroup->adt->action = nullptr; + } + } + + /* free the group tree (takes care of user count) */ + BKE_id_free(bmain, wgroup); + + /* restore external links to and from the gnode */ + + /* input links */ + if (glinks_first != nullptr) { + for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) { + if (link->fromnode->type == NODE_GROUP_INPUT) { + const char *identifier = link->fromsock->identifier; + int num_external_links = 0; + + /* find external links to this input */ + for (bNodeLink *tlink = (bNodeLink *)ntree->links.first; tlink != glinks_first->next; + tlink = tlink->next) { + if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) { + nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock); + num_external_links++; + } + } + + /* if group output is not externally linked, + * convert the constant input value to ensure somewhat consistent behavior */ + if (num_external_links == 0) { + /* TODO */ +#if 0 + bNodeSocket *sock = node_group_find_input_socket(gnode, identifier); + BLI_assert(sock); + + nodeSocketCopy( + ntree, link->tosock->new_sock, link->tonode->new_node, ntree, sock, gnode); +#endif + } + } + } + + /* Also iterate over new links to cover passthrough links. */ + glinks_last = (bNodeLink *)ntree->links.last; + + /* output links */ + for (bNodeLink *link = (bNodeLink *)ntree->links.first; link != glinks_first->next; + link = link->next) { + if (link->fromnode == gnode) { + const char *identifier = link->fromsock->identifier; + int num_internal_links = 0; + + /* find internal links to this output */ + for (bNodeLink *tlink = glinks_first->next; tlink != glinks_last->next; + tlink = tlink->next) { + /* only use active output node */ + if (tlink->tonode->type == NODE_GROUP_OUTPUT && (tlink->tonode->flag & NODE_DO_OUTPUT)) { + if (STREQ(tlink->tosock->identifier, identifier)) { + nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock); + num_internal_links++; + } + } + } + + /* if group output is not internally linked, + * convert the constant output value to ensure somewhat consistent behavior */ + if (num_internal_links == 0) { + /* TODO */ +#if 0 + bNodeSocket *sock = node_group_find_output_socket(gnode, identifier); + BLI_assert(sock); + + nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode); +#endif + } + } + } + } + + while (nodes_delayed_free) { + bNode *node = (bNode *)BLI_linklist_pop(&nodes_delayed_free); + nodeRemoveNode(bmain, ntree, node, false); + } + + /* delete the group instance and dereference group tree */ + nodeRemoveNode(bmain, ntree, gnode, true); + + ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + + return 1; +} + +static int node_group_ungroup_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + const char *node_idname = node_group_idname(C); + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + bNode *gnode = node_group_get_active(C, node_idname); + if (!gnode) { + return OPERATOR_CANCELLED; + } + + if (gnode->id && node_group_ungroup(bmain, snode->edittree, gnode)) { + ntreeUpdateTree(bmain, snode->nodetree); + } + else { + BKE_report(op->reports, RPT_WARNING, "Cannot ungroup"); + return OPERATOR_CANCELLED; + } + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_group_ungroup(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Ungroup"; + ot->description = "Ungroup selected nodes"; + ot->idname = "NODE_OT_group_ungroup"; + + /* api callbacks */ + ot->exec = node_group_ungroup_exec; + ot->poll = node_group_operator_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Separate Operator + * \{ */ + +/* returns 1 if its OK */ +static int node_group_separate_selected( + Main *bmain, bNodeTree *ntree, bNodeTree *ngroup, float offx, float offy, int make_copy) +{ + /* deselect all nodes in the target tree */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + nodeSetSelected(node, false); + } + + /* clear new pointers, set in BKE_node_copy_ex(). */ + LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { + node->new_node = nullptr; + } + + ListBase anim_basepaths = {nullptr, nullptr}; + + /* add selected nodes into the ntree */ + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ngroup->nodes) { + if (!(node->flag & NODE_SELECT)) { + continue; + } + + /* ignore interface nodes */ + if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) { + nodeSetSelected(node, false); + continue; + } + + bNode *newnode; + if (make_copy) { + /* make a copy */ + newnode = BKE_node_copy_store_new_pointers(ngroup, node, LIB_ID_COPY_DEFAULT); + } + else { + /* use the existing node */ + newnode = node; + } + + /* keep track of this node's RNA "base" path (the part of the path identifying the node) + * if the old nodetree has animation data which potentially covers this node + */ + if (ngroup->adt) { + PointerRNA ptr; + char *path; + + RNA_pointer_create(&ngroup->id, &RNA_Node, newnode, &ptr); + path = RNA_path_from_ID_to_struct(&ptr); + + if (path) { + BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path)); + } + } + + /* ensure valid parent pointers, detach if parent stays inside the group */ + if (newnode->parent && !(newnode->parent->flag & NODE_SELECT)) { + nodeDetachNode(newnode); + } + + /* migrate node */ + BLI_remlink(&ngroup->nodes, newnode); + BLI_addtail(&ntree->nodes, newnode); + + /* ensure unique node name in the node tree */ + nodeUniqueName(ntree, newnode); + + if (!newnode->parent) { + newnode->locx += offx; + newnode->locy += offy; + } + } + + /* add internal links to the ntree */ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup->links) { + const bool fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT)); + const bool toselect = (link->tonode && (link->tonode->flag & NODE_SELECT)); + + if (make_copy) { + /* make a copy of internal links */ + if (fromselect && toselect) { + nodeAddLink(ntree, + link->fromnode->new_node, + link->fromsock->new_sock, + link->tonode->new_node, + link->tosock->new_sock); + } + } + else { + /* move valid links over, delete broken links */ + if (fromselect && toselect) { + BLI_remlink(&ngroup->links, link); + BLI_addtail(&ntree->links, link); + } + else if (fromselect || toselect) { + nodeRemLink(ngroup, link); + } + } + } + + /* and copy across the animation, + * note that the animation data's action can be nullptr here */ + if (ngroup->adt) { + /* now perform the moving */ + BKE_animdata_transfer_by_basepath(bmain, &ngroup->id, &ntree->id, &anim_basepaths); + + /* paths + their wrappers need to be freed */ + LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) { + animation_basepath_change_free(basepath_change); + } + } + + ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + if (!make_copy) { + ngroup->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + } + + return 1; +} + +enum eNodeGroupSeparateType { + NODE_GS_COPY, + NODE_GS_MOVE, +}; + +/* Operator Property */ +static const EnumPropertyItem node_group_separate_types[] = { + {NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"}, + {NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static int node_group_separate_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + int type = RNA_enum_get(op->ptr, "type"); + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + /* are we inside of a group? */ + bNodeTree *ngroup = snode->edittree; + bNodeTree *nparent = ED_node_tree_get(snode, 1); + if (!nparent) { + BKE_report(op->reports, RPT_WARNING, "Not inside node group"); + return OPERATOR_CANCELLED; + } + /* get node tree offset */ + float offx, offy; + space_node_group_offset(snode, &offx, &offy); + + switch (type) { + case NODE_GS_COPY: + if (!node_group_separate_selected(bmain, nparent, ngroup, offx, offy, true)) { + BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes"); + return OPERATOR_CANCELLED; + } + break; + case NODE_GS_MOVE: + if (!node_group_separate_selected(bmain, nparent, ngroup, offx, offy, false)) { + BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes"); + return OPERATOR_CANCELLED; + } + break; + } + + /* switch to parent tree */ + ED_node_tree_pop(snode); + + ntreeUpdateTree(CTX_data_main(C), snode->nodetree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +static int node_group_separate_invoke(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *UNUSED(event)) +{ + uiPopupMenu *pup = UI_popup_menu_begin( + C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + + uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); + uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_COPY); + uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_MOVE); + + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +void NODE_OT_group_separate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Separate"; + ot->description = "Separate selected nodes from the node group"; + ot->idname = "NODE_OT_group_separate"; + + /* api callbacks */ + ot->invoke = node_group_separate_invoke; + ot->exec = node_group_separate_exec; + ot->poll = node_group_operator_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "type", node_group_separate_types, NODE_GS_COPY, "Type", ""); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Make Group Operator + * \{ */ + +static bool node_group_make_use_node(bNode *node, bNode *gnode) +{ + return (node != gnode && !ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT) && + (node->flag & NODE_SELECT)); +} + +static bool node_group_make_test_selected(bNodeTree *ntree, + bNode *gnode, + const char *ntree_idname, + struct ReportList *reports) +{ + int ok = true; + + /* make a local pseudo node tree to pass to the node poll functions */ + bNodeTree *ngroup = ntreeAddTree(nullptr, "Pseudo Node Group", ntree_idname); + + /* check poll functions for selected nodes */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node_group_make_use_node(node, gnode)) { + const char *disabled_hint = nullptr; + if (node->typeinfo->poll_instance && + !node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) { + if (disabled_hint) { + BKE_reportf(reports, + RPT_WARNING, + "Can not add node '%s' in a group:\n %s", + node->name, + disabled_hint); + } + else { + BKE_reportf(reports, RPT_WARNING, "Can not add node '%s' in a group", node->name); + } + ok = false; + break; + } + } + + node->done = 0; + } + + /* free local pseudo node tree again */ + ntreeFreeTree(ngroup); + MEM_freeN(ngroup); + if (!ok) { + return false; + } + + /* check if all connections are OK, no unselected node has both + * inputs and outputs to a selection */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (node_group_make_use_node(link->fromnode, gnode)) { + link->tonode->done |= 1; + } + if (node_group_make_use_node(link->tonode, gnode)) { + link->fromnode->done |= 2; + } + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (!(node->flag & NODE_SELECT) && node != gnode && node->done == 3) { + return false; + } + } + return true; +} + +static int node_get_selected_minmax( + bNodeTree *ntree, bNode *gnode, float *min, float *max, bool use_size) +{ + int totselect = 0; + + INIT_MINMAX2(min, max); + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node_group_make_use_node(node, gnode)) { + float loc[2]; + nodeToView(node, node->offsetx, node->offsety, &loc[0], &loc[1]); + minmax_v2v2_v2(min, max, loc); + if (use_size) { + loc[0] += node->width; + loc[1] -= node->height; + minmax_v2v2_v2(min, max, loc); + } + totselect++; + } + } + + /* sane min/max if no selected nodes */ + if (totselect == 0) { + min[0] = min[1] = max[0] = max[1] = 0.0f; + } + + return totselect; +} + +static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, bNode *gnode) +{ + Main *bmain = CTX_data_main(C); + bNodeTree *ngroup = (bNodeTree *)gnode->id; + bool expose_visible = false; + + /* XXX rough guess, not nice but we don't have access to UI constants here ... */ + static const float offsetx = 200; + static const float offsety = 0.0f; + + /* deselect all nodes in the target tree */ + LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { + nodeSetSelected(node, false); + } + + float center[2], min[2], max[2]; + const int totselect = node_get_selected_minmax(ntree, gnode, min, max, false); + add_v2_v2v2(center, min, max); + mul_v2_fl(center, 0.5f); + + float real_min[2], real_max[2]; + node_get_selected_minmax(ntree, gnode, real_min, real_max, true); + + /* auto-add interface for "solo" nodes */ + if (totselect == 1) { + expose_visible = true; + } + + ListBase anim_basepaths = {nullptr, nullptr}; + + /* move nodes over */ + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { + if (node_group_make_use_node(node, gnode)) { + /* keep track of this node's RNA "base" path (the part of the pat identifying the node) + * if the old nodetree has animation data which potentially covers this node + */ + if (ntree->adt) { + PointerRNA ptr; + char *path; + + RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr); + path = RNA_path_from_ID_to_struct(&ptr); + + if (path) { + BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path)); + } + } + + /* ensure valid parent pointers, detach if parent stays outside the group */ + if (node->parent && !(node->parent->flag & NODE_SELECT)) { + nodeDetachNode(node); + } + + /* change node-collection membership */ + BLI_remlink(&ntree->nodes, node); + BLI_addtail(&ngroup->nodes, node); + + /* ensure unique node name in the ngroup */ + nodeUniqueName(ngroup, node); + } + } + + /* move animation data over */ + if (ntree->adt) { + BKE_animdata_transfer_by_basepath(bmain, &ntree->id, &ngroup->id, &anim_basepaths); + + /* paths + their wrappers need to be freed */ + LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) { + animation_basepath_change_free(basepath_change); + } + } + + /* node groups don't use internal cached data */ + ntreeFreeCache(ngroup); + + /* create input node */ + bNode *input_node = nodeAddStaticNode(C, ngroup, NODE_GROUP_INPUT); + input_node->locx = real_min[0] - center[0] - offsetx; + input_node->locy = -offsety; + + /* create output node */ + bNode *output_node = nodeAddStaticNode(C, ngroup, NODE_GROUP_OUTPUT); + output_node->locx = real_max[0] - center[0] + offsetx * 0.25f; + output_node->locy = -offsety; + + /* relink external sockets */ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { + int fromselect = node_group_make_use_node(link->fromnode, gnode); + int toselect = node_group_make_use_node(link->tonode, gnode); + + if ((fromselect && link->tonode == gnode) || (toselect && link->fromnode == gnode)) { + /* remove all links to/from the gnode. + * this can remove link information, but there's no general way to preserve it. + */ + nodeRemLink(ntree, link); + } + else if (toselect && !fromselect) { + bNodeSocket *link_sock; + bNode *link_node; + node_socket_skip_reroutes(&ntree->links, link->tonode, link->tosock, &link_node, &link_sock); + bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link_node, link_sock); + + /* update the group node and interface node sockets, + * so the new interface socket can be linked. + */ + node_group_update(ntree, gnode); + node_group_input_update(ngroup, input_node); + + /* create new internal link */ + bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier); + nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock); + + /* redirect external link */ + link->tonode = gnode; + link->tosock = node_group_find_input_socket(gnode, iosock->identifier); + } + else if (fromselect && !toselect) { + /* First check whether the source of this link is already connected to an output. + * If yes, reuse that output instead of duplicating it. */ + bool connected = false; + LISTBASE_FOREACH (bNodeLink *, olink, &ngroup->links) { + if (olink->fromsock == link->fromsock && olink->tonode == output_node) { + bNodeSocket *output_sock = node_group_find_output_socket(gnode, + olink->tosock->identifier); + link->fromnode = gnode; + link->fromsock = output_sock; + connected = true; + } + } + + if (!connected) { + bNodeSocket *link_sock; + bNode *link_node; + node_socket_skip_reroutes( + &ntree->links, link->fromnode, link->fromsock, &link_node, &link_sock); + bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link_node, link_sock); + + /* update the group node and interface node sockets, + * so the new interface socket can be linked. + */ + node_group_update(ntree, gnode); + node_group_output_update(ngroup, output_node); + + /* create new internal link */ + bNodeSocket *output_sock = node_group_output_find_socket(output_node, iosock->identifier); + nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock); + + /* redirect external link */ + link->fromnode = gnode; + link->fromsock = node_group_find_output_socket(gnode, iosock->identifier); + } + } + } + + /* move internal links */ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { + int fromselect = node_group_make_use_node(link->fromnode, gnode); + int toselect = node_group_make_use_node(link->tonode, gnode); + + if (fromselect && toselect) { + BLI_remlink(&ntree->links, link); + BLI_addtail(&ngroup->links, link); + } + } + + /* move nodes in the group to the center */ + LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { + if (node_group_make_use_node(node, gnode) && !node->parent) { + node->locx -= center[0]; + node->locy -= center[1]; + } + } + + /* expose all unlinked sockets too but only the visible ones*/ + if (expose_visible) { + LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { + if (node_group_make_use_node(node, gnode)) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + bool skip = false; + LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) { + if (link->tosock == sock) { + skip = true; + break; + } + } + if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) { + skip = true; + } + if (skip) { + continue; + } + + bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock); + + node_group_input_update(ngroup, input_node); + + /* create new internal link */ + bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier); + nodeAddLink(ngroup, input_node, input_sock, node, sock); + } + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + bool skip = false; + LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) { + if (link->fromsock == sock) { + skip = true; + } + } + if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) { + skip = true; + } + if (skip) { + continue; + } + + bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock); + + node_group_output_update(ngroup, output_node); + + /* create new internal link */ + bNodeSocket *output_sock = node_group_output_find_socket(output_node, + iosock->identifier); + nodeAddLink(ngroup, node, sock, output_node, output_sock); + } + } + } + } + + /* update of the group tree */ + ngroup->update |= NTREE_UPDATE | NTREE_UPDATE_LINKS; + /* update of the tree containing the group instance node */ + ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; +} + +static bNode *node_group_make_from_selected(const bContext *C, + bNodeTree *ntree, + const char *ntype, + const char *ntreetype) +{ + Main *bmain = CTX_data_main(C); + + float min[2], max[2]; + const int totselect = node_get_selected_minmax(ntree, nullptr, min, max, false); + /* don't make empty group */ + if (totselect == 0) { + return nullptr; + } + + /* new nodetree */ + bNodeTree *ngroup = ntreeAddTree(bmain, "NodeGroup", ntreetype); + + /* make group node */ + bNode *gnode = nodeAddNode(C, ntree, ntype); + gnode->id = (ID *)ngroup; + + gnode->locx = 0.5f * (min[0] + max[0]); + gnode->locy = 0.5f * (min[1] + max[1]); + + node_group_make_insert_selected(C, ntree, gnode); + + /* update of the tree containing the group instance node */ + ntree->update |= NTREE_UPDATE_NODES; + + return gnode; +} + +static int node_group_make_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + const char *ntree_idname = group_ntree_idname(C); + const char *node_idname = node_group_idname(C); + Main *bmain = CTX_data_main(C); + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + if (!node_group_make_test_selected(ntree, nullptr, ntree_idname, op->reports)) { + return OPERATOR_CANCELLED; + } + + bNode *gnode = node_group_make_from_selected(C, ntree, node_idname, ntree_idname); + + if (gnode) { + bNodeTree *ngroup = (bNodeTree *)gnode->id; + + nodeSetActive(ntree, gnode); + if (ngroup) { + ED_node_tree_push(snode, ngroup, gnode); + LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { + sort_multi_input_socket_links(snode, node, nullptr, nullptr); + } + ntreeUpdateTree(bmain, ngroup); + } + } + + ntreeUpdateTree(bmain, ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + /* We broke relations in node tree, need to rebuild them in the graphs. */ + DEG_relations_tag_update(bmain); + + return OPERATOR_FINISHED; +} + +void NODE_OT_group_make(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Make Group"; + ot->description = "Make group from selected nodes"; + ot->idname = "NODE_OT_group_make"; + + /* api callbacks */ + ot->exec = node_group_make_exec; + ot->poll = node_group_operator_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Group Insert Operator + * \{ */ + +static int node_group_insert_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + const char *node_idname = node_group_idname(C); + Main *bmain = CTX_data_main(C); + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + bNode *gnode = node_group_get_active(C, node_idname); + + if (!gnode || !gnode->id) { + return OPERATOR_CANCELLED; + } + + bNodeTree *ngroup = (bNodeTree *)gnode->id; + if (!node_group_make_test_selected(ntree, gnode, ngroup->idname, op->reports)) { + return OPERATOR_CANCELLED; + } + + node_group_make_insert_selected(C, ntree, gnode); + + nodeSetActive(ntree, gnode); + ED_node_tree_push(snode, ngroup, gnode); + ntreeUpdateTree(bmain, ngroup); + + ntreeUpdateTree(bmain, ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_group_insert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Group Insert"; + ot->description = "Insert selected nodes into a node group"; + ot->idname = "NODE_OT_group_insert"; + + /* api callbacks */ + ot->exec = node_group_insert_exec; + ot->poll = node_group_operator_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c deleted file mode 100644 index 28c660b0632..00000000000 --- a/source/blender/editors/space_node/node_relationships.c +++ /dev/null @@ -1,2316 +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. - */ - -/** \file - * \ingroup spnode - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_anim_types.h" -#include "DNA_node_types.h" - -#include "BLI_blenlib.h" -#include "BLI_easing.h" -#include "BLI_math.h" - -#include "BKE_anim_data.h" -#include "BKE_context.h" -#include "BKE_curve.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_node.h" - -#include "ED_node.h" /* own include */ -#include "ED_render.h" -#include "ED_screen.h" -#include "ED_util.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "UI_resources.h" -#include "UI_view2d.h" - -#include "BLT_translation.h" - -#include "node_intern.h" /* own include */ - -/* ****************** Relations helpers *********************** */ - -static bool ntree_has_drivers(bNodeTree *ntree) -{ - const AnimData *adt = BKE_animdata_from_id(&ntree->id); - if (adt == NULL) { - return false; - } - return !BLI_listbase_is_empty(&adt->drivers); -} - -static bool ntree_check_nodes_connected_dfs(bNodeTree *ntree, bNode *from, bNode *to) -{ - if (from->flag & NODE_TEST) { - return false; - } - from->flag |= NODE_TEST; - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - if (link->fromnode == from) { - if (link->tonode == to) { - return true; - } - - if (ntree_check_nodes_connected_dfs(ntree, link->tonode, to)) { - return true; - } - } - } - return false; -} - -static bool ntree_check_nodes_connected(bNodeTree *ntree, bNode *from, bNode *to) -{ - if (from == to) { - return true; - } - ntreeNodeFlagSet(ntree, NODE_TEST, false); - return ntree_check_nodes_connected_dfs(ntree, from, to); -} - -static bool node_group_has_output_dfs(bNode *node) -{ - bNodeTree *ntree = (bNodeTree *)node->id; - if (ntree->id.tag & LIB_TAG_DOIT) { - return false; - } - ntree->id.tag |= LIB_TAG_DOIT; - for (bNode *current_node = ntree->nodes.first; current_node != NULL; - current_node = current_node->next) { - if (current_node->type == NODE_GROUP) { - if (current_node->id && node_group_has_output_dfs(current_node)) { - return true; - } - } - if (current_node->flag & NODE_DO_OUTPUT && current_node->type != NODE_GROUP_OUTPUT) { - return true; - } - } - return false; -} - -static bool node_group_has_output(Main *bmain, bNode *node) -{ - BLI_assert(ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)); - bNodeTree *ntree = (bNodeTree *)node->id; - if (ntree == NULL) { - return false; - } - BKE_main_id_tag_listbase(&bmain->nodetrees, LIB_TAG_DOIT, false); - return node_group_has_output_dfs(node); -} - -bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node) -{ - /* Special case for drivers: if node tree has any drivers we assume it is - * always to be tagged for update when node changes. Otherwise we will be - * doomed to do some deep and nasty deep search of indirect dependencies, - * which will be too complicated without real benefit. - */ - if (ntree_has_drivers(ntree)) { - return true; - } - LISTBASE_FOREACH (bNode *, current_node, &ntree->nodes) { - /* Special case for group nodes -- if modified node connected to a group - * with active output inside we consider refresh is needed. - * - * We could make check more grained here by taking which socket the node - * is connected to and so eventually. - */ - if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { - if (current_node->id != NULL && ntree_has_drivers((bNodeTree *)current_node->id)) { - return true; - } - if (ntree_check_nodes_connected(ntree, node, current_node) && - node_group_has_output(bmain, current_node)) { - return true; - } - } - if (current_node->flag & NODE_DO_OUTPUT) { - if (ntree_check_nodes_connected(ntree, node, current_node)) { - return true; - } - } - } - return false; -} - -/* ****************** Add *********************** */ - -typedef struct bNodeListItem { - struct bNodeListItem *next, *prev; - struct bNode *node; -} bNodeListItem; - -typedef struct NodeInsertOfsData { - bNodeTree *ntree; - bNode *insert; /* inserted node */ - bNode *prev, *next; /* prev/next node in the chain */ - bNode *insert_parent; - - wmTimer *anim_timer; - - float offset_x; /* offset to apply to node chain */ -} NodeInsertOfsData; - -static void clear_picking_highlight(ListBase *links) -{ - LISTBASE_FOREACH (bNodeLink *, link, links) { - link->flag &= ~NODE_LINK_TEMP_HIGHLIGHT; - } -} - -static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bNodeSocket *sock) -{ - LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data"); - bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link"); - linkdata->data = oplink; - if (sock->in_out == SOCK_OUT) { - oplink->fromnode = node; - oplink->fromsock = sock; - } - else { - oplink->tonode = node; - oplink->tosock = sock; - } - oplink->flag |= NODE_LINK_VALID; - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, snode->edittree, node)) { - oplink->flag |= NODE_LINK_TEST; - } - return linkdata; -} - -static void pick_link(const bContext *C, - wmOperator *op, - bNodeLinkDrag *nldrag, - SpaceNode *snode, - bNode *node, - bNodeLink *link_to_pick) -{ - clear_picking_highlight(&snode->edittree->links); - RNA_boolean_set(op->ptr, "has_link_picked", true); - - Main *bmain = CTX_data_main(C); - LinkData *linkdata = create_drag_link( - bmain, snode, link_to_pick->fromnode, link_to_pick->fromsock); - - BLI_addtail(&nldrag->links, linkdata); - nodeRemLink(snode->edittree, link_to_pick); - - BLI_assert(nldrag->last_node_hovered_while_dragging_a_link != NULL); - - sort_multi_input_socket_links( - snode, nldrag->last_node_hovered_while_dragging_a_link, NULL, NULL); - - /* Send changed event to original link->tonode. */ - if (node) { - snode_update(snode, node); - } -} - -static void pick_input_link_by_link_intersect(const bContext *C, - wmOperator *op, - bNodeLinkDrag *nldrag, - const float *cursor) -{ - SpaceNode *snode = CTX_wm_space_node(C); - const ARegion *region = CTX_wm_region(C); - const View2D *v2d = ®ion->v2d; - - float drag_start[2]; - RNA_float_get_array(op->ptr, "drag_start", drag_start); - bNode *node; - bNodeSocket *socket; - node_find_indicated_socket(snode, &node, &socket, drag_start, SOCK_IN); - - /* Distance to test overlapping of cursor on link. */ - const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC; - - const int resolution = NODE_LINK_RESOL; - - bNodeLink *link_to_pick = NULL; - clear_picking_highlight(&snode->edittree->links); - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (link->tosock == socket) { - /* Test if the cursor is near a link. */ - float vec[4][2]; - node_link_bezier_handles(v2d, snode, link, vec); - - float data[NODE_LINK_RESOL * 2 + 2]; - BKE_curve_forward_diff_bezier( - vec[0][0], vec[1][0], vec[2][0], vec[3][0], data, resolution, sizeof(float[2])); - BKE_curve_forward_diff_bezier( - vec[0][1], vec[1][1], vec[2][1], vec[3][1], data + 1, resolution, sizeof(float[2])); - - for (int i = 0; i < resolution * 2; i += 2) { - float *l1 = &data[i]; - float *l2 = &data[i + 2]; - float distance = dist_squared_to_line_segment_v2(cursor, l1, l2); - if (distance < cursor_link_touch_distance) { - link_to_pick = link; - nldrag->last_picked_multi_input_socket_link = link_to_pick; - } - } - } - } - - /* If no linked was picked in this call, try using the one picked in the previous call. - * Not essential for the basic behavior, but can make interaction feel a bit better if - * the mouse moves to the right and loses the "selection." */ - if (!link_to_pick) { - bNodeLink *last_picked_link = nldrag->last_picked_multi_input_socket_link; - if (last_picked_link) { - link_to_pick = last_picked_link; - } - } - - if (link_to_pick) { - /* Highlight is set here and cleared in the next iteration or if the operation finishes. */ - link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT; - ED_area_tag_redraw(CTX_wm_area(C)); - - if (!node_find_indicated_socket(snode, &node, &socket, cursor, SOCK_IN)) { - pick_link(C, op, nldrag, snode, node, link_to_pick); - } - } -} - -static int sort_nodes_locx(const void *a, const void *b) -{ - const bNodeListItem *nli1 = a; - const bNodeListItem *nli2 = b; - const bNode *node1 = nli1->node; - const bNode *node2 = nli2->node; - - if (node1->locx > node2->locx) { - return 1; - } - return 0; -} - -static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used) -{ - if (nodeSocketIsHidden(sock)) { - return 0; - } - - if (!allow_used && (sock->flag & SOCK_IN_USE)) { - return 0; - } - - return 1; -} - -static bNodeSocket *best_socket_output(bNodeTree *ntree, - bNode *node, - bNodeSocket *sock_target, - const bool allow_multiple) -{ - /* first look for selected output */ - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - if (!socket_is_available(ntree, sock, allow_multiple)) { - continue; - } - - if (sock->flag & SELECT) { - return sock; - } - } - - /* try to find a socket with a matching name */ - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - if (!socket_is_available(ntree, sock, allow_multiple)) { - continue; - } - - /* check for same types */ - if (sock->type == sock_target->type) { - if (STREQ(sock->name, sock_target->name)) { - return sock; - } - } - } - - /* otherwise settle for the first available socket of the right type */ - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - if (!socket_is_available(ntree, sock, allow_multiple)) { - continue; - } - - /* check for same types */ - if (sock->type == sock_target->type) { - return sock; - } - } - - /* Always allow linking to an reroute node. The socket type of the reroute sockets might change - * after the link has been created. */ - if (node->type == NODE_REROUTE) { - return node->outputs.first; - } - - return NULL; -} - -/* this is a bit complicated, but designed to prioritize finding - * sockets of higher types, such as image, first */ -static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace) -{ - int maxtype = 0; - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - maxtype = max_ii(sock->type, maxtype); - } - - /* find sockets of higher 'types' first (i.e. image) */ - int a = 0; - for (int socktype = maxtype; socktype >= 0; socktype--) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (!socket_is_available(ntree, sock, replace)) { - a++; - continue; - } - - if (sock->type == socktype) { - /* increment to make sure we don't keep finding - * the same socket on every attempt running this function */ - a++; - if (a > num) { - return sock; - } - } - } - } - - return NULL; -} - -static bool snode_autoconnect_input(SpaceNode *snode, - bNode *node_fr, - bNodeSocket *sock_fr, - bNode *node_to, - bNodeSocket *sock_to, - int replace) -{ - bNodeTree *ntree = snode->edittree; - - /* then we can connect */ - if (replace) { - nodeRemSocketLinks(ntree, sock_to); - } - - nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to); - return true; -} - -typedef struct LinkAndPosition { - struct bNodeLink *link; - float multi_socket_position[2]; -} LinkAndPosition; - -static int compare_link_by_y_position(const void *a, const void *b) -{ - const LinkAndPosition *link_and_position_a = *(const LinkAndPosition **)a; - const LinkAndPosition *link_and_position_b = *(const LinkAndPosition **)b; - - BLI_assert(link_and_position_a->link->tosock == link_and_position_b->link->tosock); - const float link_a_y = link_and_position_a->multi_socket_position[1]; - const float link_b_y = link_and_position_b->multi_socket_position[1]; - return link_a_y > link_b_y ? 1 : -1; -} - -void sort_multi_input_socket_links(SpaceNode *snode, - bNode *node, - bNodeLink *drag_link, - float cursor[2]) -{ - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (!(socket->flag & SOCK_MULTI_INPUT)) { - continue; - } - /* The total is calculated in #node_update_nodetree, which runs before this draw step. */ - int total_inputs = socket->total_inputs + 1; - struct LinkAndPosition **input_links = MEM_malloc_arrayN( - total_inputs, sizeof(LinkAndPosition *), __func__); - - int index = 0; - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (link->tosock == socket) { - struct LinkAndPosition *link_and_position = MEM_callocN(sizeof(struct LinkAndPosition), - __func__); - link_and_position->link = link; - node_link_calculate_multi_input_position(link->tosock->locx, - link->tosock->locy, - link->multi_input_socket_index, - link->tosock->total_inputs, - link_and_position->multi_socket_position); - input_links[index] = link_and_position; - index++; - } - } - - if (drag_link) { - LinkAndPosition *link_and_position = MEM_callocN(sizeof(LinkAndPosition), __func__); - link_and_position->link = drag_link; - copy_v2_v2(link_and_position->multi_socket_position, cursor); - input_links[index] = link_and_position; - index++; - } - - qsort(input_links, index, sizeof(bNodeLink *), compare_link_by_y_position); - - for (int i = 0; i < index; i++) { - input_links[i]->link->multi_input_socket_index = i; - } - - for (int i = 0; i < index; i++) { - if (input_links[i]) { - MEM_freeN(input_links[i]); - } - } - MEM_freeN(input_links); - } -} - -static void snode_autoconnect(Main *bmain, - SpaceNode *snode, - const bool allow_multiple, - const bool replace) -{ - bNodeTree *ntree = snode->edittree; - ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list"); - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->flag & NODE_SELECT) { - bNodeListItem *nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item"); - nli->node = node; - BLI_addtail(nodelist, nli); - } - } - - /* sort nodes left to right */ - BLI_listbase_sort(nodelist, sort_nodes_locx); - - int numlinks = 0; - LISTBASE_FOREACH (bNodeListItem *, nli, nodelist) { - bool has_selected_inputs = false; - - if (nli->next == NULL) { - break; - } - - bNode *node_fr = nli->node; - bNode *node_to = nli->next->node; - /* corner case: input/output node aligned the wrong way around (T47729) */ - if (BLI_listbase_is_empty(&node_to->inputs) || BLI_listbase_is_empty(&node_fr->outputs)) { - SWAP(bNode *, node_fr, node_to); - } - - /* if there are selected sockets, connect those */ - LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) { - if (sock_to->flag & SELECT) { - has_selected_inputs = 1; - - if (!socket_is_available(ntree, sock_to, replace)) { - continue; - } - - /* check for an appropriate output socket to connect from */ - bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple); - if (!sock_fr) { - continue; - } - - if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) { - numlinks++; - } - } - } - - if (!has_selected_inputs) { - /* no selected inputs, connect by finding suitable match */ - int num_inputs = BLI_listbase_count(&node_to->inputs); - - for (int i = 0; i < num_inputs; i++) { - - /* find the best guess input socket */ - bNodeSocket *sock_to = best_socket_input(ntree, node_to, i, replace); - if (!sock_to) { - continue; - } - - /* check for an appropriate output socket to connect from */ - bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple); - if (!sock_fr) { - continue; - } - - if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) { - numlinks++; - break; - } - } - } - } - - if (numlinks > 0) { - ntreeUpdateTree(bmain, ntree); - } - - BLI_freelistN(nodelist); - MEM_freeN(nodelist); -} - -/* *************************** link viewer op ******************** */ - -static int node_link_viewer(const bContext *C, bNode *tonode) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - /* context check */ - if (tonode == NULL || BLI_listbase_is_empty(&tonode->outputs)) { - return OPERATOR_CANCELLED; - } - if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { - return OPERATOR_CANCELLED; - } - - /* get viewer */ - bNode *viewer_node = NULL; - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { - if (node->flag & NODE_DO_OUTPUT) { - viewer_node = node; - break; - } - } - } - /* no viewer, we make one active */ - if (viewer_node == NULL) { - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { - node->flag |= NODE_DO_OUTPUT; - viewer_node = node; - break; - } - } - } - - bNodeSocket *sock = NULL; - bNodeLink *link = NULL; - - /* try to find an already connected socket to cycle to the next */ - if (viewer_node) { - link = NULL; - - for (link = snode->edittree->links.first; link; link = link->next) { - if (link->tonode == viewer_node && link->fromnode == tonode) { - if (link->tosock == viewer_node->inputs.first) { - break; - } - } - } - if (link) { - /* unlink existing connection */ - sock = link->fromsock; - nodeRemLink(snode->edittree, link); - - /* find a socket after the previously connected socket */ - for (sock = sock->next; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; - } - } - } - } - - if (tonode) { - /* Find a selected socket that overrides the socket to connect to */ - LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { - if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { - sock = sock2; - break; - } - } - } - - /* find a socket starting from the first socket */ - if (!sock) { - for (sock = tonode->outputs.first; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; - } - } - } - - if (sock) { - /* add a new viewer if none exists yet */ - if (!viewer_node) { - /* XXX location is a quick hack, just place it next to the linked socket */ - viewer_node = node_add_node(C, NULL, CMP_NODE_VIEWER, sock->locx + 100, sock->locy); - if (!viewer_node) { - return OPERATOR_CANCELLED; - } - - link = NULL; - } - else { - /* get link to viewer */ - for (link = snode->edittree->links.first; link; link = link->next) { - if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) { - break; - } - } - } - - if (link == NULL) { - nodeAddLink(snode->edittree, tonode, sock, viewer_node, viewer_node->inputs.first); - } - else { - link->fromnode = tonode; - link->fromsock = sock; - /* make sure the dependency sorting is updated */ - snode->edittree->update |= NTREE_UPDATE_LINKS; - } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - snode_update(snode, viewer_node); - } - - return OPERATOR_FINISHED; -} - -static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNode *node = nodeGetActive(snode->edittree); - - if (!node) { - return OPERATOR_CANCELLED; - } - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - if (node_link_viewer(C, node) == OPERATOR_CANCELLED) { - return OPERATOR_CANCELLED; - } - - snode_notify(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_link_viewer(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Link to Viewer Node"; - ot->description = "Link to viewer node"; - ot->idname = "NODE_OT_link_viewer"; - - /* api callbacks */ - ot->exec = node_active_link_viewer_exec; - ot->poll = composite_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* *************************** add link op ******************** */ - -static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag)) -{ - char header[UI_MAX_DRAW_STR]; - - BLI_strncpy(header, TIP_("LMB: drag node link, RMB: cancel"), sizeof(header)); - ED_workspace_status_text(C, header); -} - -static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket) -{ - int count = 0; - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { - if (ELEM(socket, link->fromsock, link->tosock)) { - count++; - } - } - return count; -} - -static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link) -{ - bNodeTree *ntree = snode->edittree; - bNodeSocket *from = link->fromsock, *to = link->tosock; - int to_count = node_count_links(ntree, to); - int from_count = node_count_links(ntree, from); - int to_link_limit = nodeSocketLinkLimit(to); - int from_link_limit = nodeSocketLinkLimit(from); - - LISTBASE_FOREACH_MUTABLE (bNodeLink *, tlink, &ntree->links) { - if (tlink == link) { - continue; - } - - if (tlink && tlink->fromsock == from) { - if (from_count > from_link_limit) { - nodeRemLink(ntree, tlink); - tlink = NULL; - from_count--; - } - } - - if (tlink && tlink->tosock == to) { - if (to_count > to_link_limit) { - nodeRemLink(ntree, tlink); - tlink = NULL; - to_count--; - } - else if (tlink->fromsock == from) { - /* Also remove link if it comes from the same output. */ - nodeRemLink(ntree, tlink); - tlink = NULL; - to_count--; - from_count--; - } - } - } -} - -static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - bNodeLinkDrag *nldrag = op->customdata; - bool do_tag_update = false; - - /* avoid updates while applying links */ - ntree->is_updating = true; - LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; - - /* See note below, but basically TEST flag means that the link - * was connected to output (or to a node which affects the - * output). - */ - do_tag_update |= (link->flag & NODE_LINK_TEST) != 0; - - if (apply_links && link->tosock && link->fromsock) { - /* before actually adding the link, - * let nodes perform special link insertion handling - */ - if (link->fromnode->typeinfo->insert_link) { - link->fromnode->typeinfo->insert_link(ntree, link->fromnode, link); - } - if (link->tonode->typeinfo->insert_link) { - link->tonode->typeinfo->insert_link(ntree, link->tonode, link); - } - - /* add link to the node tree */ - BLI_addtail(&ntree->links, link); - - ntree->update |= NTREE_UPDATE_LINKS; - - /* tag tonode for update */ - link->tonode->update |= NODE_UPDATE; - - /* we might need to remove a link */ - node_remove_extra_links(snode, link); - - if (link->tonode) { - do_tag_update |= (do_tag_update || node_connected_to_output(bmain, ntree, link->tonode)); - } - } - else { - nodeRemLink(ntree, link); - } - } - ntree->is_updating = false; - - ntreeUpdateTree(bmain, ntree); - snode_notify(C, snode); - if (do_tag_update) { - snode_dag_update(C, snode); - } - - BLI_remlink(&snode->runtime->linkdrag, nldrag); - /* links->data pointers are either held by the tree or freed already */ - BLI_freelistN(&nldrag->links); - MEM_freeN(nldrag); -} - -static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2]) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeLinkDrag *nldrag = op->customdata; - - if (nldrag->in_out == SOCK_OUT) { - bNode *tnode; - bNodeSocket *tsock = NULL; - if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) { - LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; - - /* skip if socket is on the same node as the fromsock */ - if (tnode && link->fromnode == tnode) { - continue; - } - - /* Skip if tsock is already linked with this output. */ - bNodeLink *existing_link_connected_to_fromsock = NULL; - LISTBASE_FOREACH (bNodeLink *, existing_link, &snode->edittree->links) { - if (existing_link->fromsock == link->fromsock && existing_link->tosock == tsock) { - existing_link_connected_to_fromsock = existing_link; - break; - } - } - - /* attach links to the socket */ - link->tonode = tnode; - link->tosock = tsock; - nldrag->last_node_hovered_while_dragging_a_link = tnode; - if (existing_link_connected_to_fromsock) { - link->multi_input_socket_index = - existing_link_connected_to_fromsock->multi_input_socket_index; - continue; - } - if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) { - sort_multi_input_socket_links(snode, tnode, link, cursor); - } - } - } - else { - LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; - if (nldrag->last_node_hovered_while_dragging_a_link) { - sort_multi_input_socket_links( - snode, nldrag->last_node_hovered_while_dragging_a_link, NULL, cursor); - } - link->tonode = NULL; - link->tosock = NULL; - } - } - } - else { - bNode *tnode; - bNodeSocket *tsock = NULL; - if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) { - LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; - - /* skip if this is already the target socket */ - if (link->fromsock == tsock) { - continue; - } - /* skip if socket is on the same node as the fromsock */ - if (tnode && link->tonode == tnode) { - continue; - } - - /* attach links to the socket */ - link->fromnode = tnode; - link->fromsock = tsock; - } - } - else { - LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = linkdata->data; - - link->fromnode = NULL; - link->fromsock = NULL; - } - } - } -} - -/* Loop that adds a node-link, called by function below. */ -/* in_out = starting socket */ -static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - bNodeLinkDrag *nldrag = op->customdata; - ARegion *region = CTX_wm_region(C); - float cursor[2]; - - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); - - switch (event->type) { - case MOUSEMOVE: - if (nldrag->from_multi_input_socket && !RNA_boolean_get(op->ptr, "has_link_picked")) { - pick_input_link_by_link_intersect(C, op, nldrag, cursor); - } - else { - node_link_find_socket(C, op, cursor); - - node_link_update_header(C, nldrag); - ED_region_tag_redraw(region); - } - break; - - case LEFTMOUSE: - case RIGHTMOUSE: - case MIDDLEMOUSE: { - if (event->val == KM_RELEASE) { - node_link_exit(C, op, true); - - ED_workspace_status_text(C, NULL); - ED_region_tag_redraw(region); - SpaceNode *snode = CTX_wm_space_node(C); - clear_picking_highlight(&snode->edittree->links); - return OPERATOR_FINISHED; - } - break; - } - } - - return OPERATOR_RUNNING_MODAL; -} - -/* return 1 when socket clicked */ -static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor[2], bool detach) -{ - bNodeLinkDrag *nldrag = NULL; - - /* output indicated? */ - bNode *node; - bNodeSocket *sock; - if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) { - nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata"); - - const int num_links = nodeCountSocketLinks(snode->edittree, sock); - int link_limit = nodeSocketLinkLimit(sock); - if (num_links > 0 && (num_links >= link_limit || detach)) { - /* dragged links are fixed on input side */ - nldrag->in_out = SOCK_IN; - /* detach current links and store them in the operator data */ - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { - if (link->fromsock == sock) { - LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data"); - bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link"); - linkdata->data = oplink; - *oplink = *link; - oplink->next = oplink->prev = NULL; - oplink->flag |= NODE_LINK_VALID; - - /* The link could be disconnected and in that case we - * wouldn't be able to check whether tag update is - * needed or not when releasing mouse button. So we - * cache whether the link affects output or not here - * using TEST flag. - */ - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, snode->edittree, link->tonode)) { - oplink->flag |= NODE_LINK_TEST; - } - - BLI_addtail(&nldrag->links, linkdata); - nodeRemLink(snode->edittree, link); - } - } - } - else { - /* dragged links are fixed on output side */ - nldrag->in_out = SOCK_OUT; - /* create a new link */ - LinkData *linkdata = create_drag_link(bmain, snode, node, sock); - - BLI_addtail(&nldrag->links, linkdata); - } - } - /* or an input? */ - else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) { - nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata"); - nldrag->last_node_hovered_while_dragging_a_link = node; - - const int num_links = nodeCountSocketLinks(snode->edittree, sock); - if (num_links > 0) { - /* dragged links are fixed on output side */ - nldrag->in_out = SOCK_OUT; - /* detach current links and store them in the operator data */ - bNodeLink *link_to_pick; - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { - if (link->tosock == sock) { - if (sock->flag & SOCK_MULTI_INPUT) { - nldrag->from_multi_input_socket = true; - } - link_to_pick = link; - } - } - - if (link_to_pick != NULL && !nldrag->from_multi_input_socket) { - LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data"); - bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link"); - linkdata->data = oplink; - *oplink = *link_to_pick; - oplink->next = oplink->prev = NULL; - oplink->flag |= NODE_LINK_VALID; - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) { - oplink->flag |= NODE_LINK_TEST; - } - - BLI_addtail(&nldrag->links, linkdata); - nodeRemLink(snode->edittree, link_to_pick); - - /* send changed event to original link->tonode */ - if (node) { - snode_update(snode, node); - } - } - } - else { - /* dragged links are fixed on input side */ - nldrag->in_out = SOCK_IN; - /* create a new link */ - LinkData *linkdata = create_drag_link(bmain, snode, node, sock); - - BLI_addtail(&nldrag->links, linkdata); - } - } - - return nldrag; -} - -static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - - bool detach = RNA_boolean_get(op->ptr, "detach"); - - float cursor[2]; - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); - RNA_float_set_array(op->ptr, "drag_start", cursor); - RNA_boolean_set(op->ptr, "has_link_picked", false); - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - bNodeLinkDrag *nldrag = node_link_init(bmain, snode, cursor, detach); - - if (nldrag) { - op->customdata = nldrag; - BLI_addtail(&snode->runtime->linkdrag, nldrag); - - /* add modal handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; -} - -static void node_link_cancel(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeLinkDrag *nldrag = op->customdata; - - BLI_remlink(&snode->runtime->linkdrag, nldrag); - - BLI_freelistN(&nldrag->links); - MEM_freeN(nldrag); - clear_picking_highlight(&snode->edittree->links); -} - -void NODE_OT_link(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Link Nodes"; - ot->idname = "NODE_OT_link"; - ot->description = "Use the mouse to create a link between two nodes"; - - /* api callbacks */ - ot->invoke = node_link_invoke; - ot->modal = node_link_modal; - // ot->exec = node_link_exec; - ot->poll = ED_operator_node_editable; - ot->cancel = node_link_cancel; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - - PropertyRNA *prop; - - RNA_def_boolean(ot->srna, "detach", false, "Detach", "Detach and redirect existing links"); - prop = RNA_def_boolean( - ot->srna, - "has_link_picked", - false, - "Has Link Picked", - "The operation has placed a link. Only used for multi-input sockets, where the " - "link is picked later"); - RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_float_array(ot->srna, - "drag_start", - 2, - 0, - -UI_PRECISION_FLOAT_MAX, - UI_PRECISION_FLOAT_MAX, - "Drag Start", - "The position of the mouse cursor at the start of the operation", - -UI_PRECISION_FLOAT_MAX, - UI_PRECISION_FLOAT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_property_flag(prop, PROP_HIDDEN); -} - -/* ********************** Make Link operator ***************** */ - -/* makes a link between selected output and input sockets */ -static int node_make_link_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - const bool replace = RNA_boolean_get(op->ptr, "replace"); - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - snode_autoconnect(bmain, snode, 1, replace); - - /* deselect sockets after linking */ - node_deselect_all_input_sockets(snode, 0); - node_deselect_all_output_sockets(snode, 0); - - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_link_make(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Make Links"; - ot->description = "Makes a link between selected output in input sockets"; - ot->idname = "NODE_OT_link_make"; - - /* callbacks */ - ot->exec = node_make_link_exec; - /* XXX we need a special poll which checks that there are selected input/output sockets. */ - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean( - ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links"); -} - -/* ********************** Node Link Intersect ***************** */ -static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int tot) -{ - float coord_array[NODE_LINK_RESOL + 1][2]; - - if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) { - for (int i = 0; i < tot - 1; i++) { - for (int b = 0; b < NODE_LINK_RESOL; b++) { - if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) { - return 1; - } - } - } - } - return 0; -} - -/* ********************** Cut Link operator ***************** */ -static int cut_links_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - bool do_tag_update = false; - - int i = 0; - float mcoords[256][2]; - RNA_BEGIN (op->ptr, itemptr, "path") { - float loc[2]; - - RNA_float_get_array(&itemptr, "loc", loc); - UI_view2d_region_to_view( - ®ion->v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]); - i++; - if (i >= 256) { - break; - } - } - RNA_END; - - if (i > 1) { - bool found = false; - - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { - continue; - } - - if (node_links_intersect(link, mcoords, i)) { - - if (found == false) { - /* TODO(sergey): Why did we kill jobs twice? */ - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - found = true; - } - - do_tag_update |= (do_tag_update || - node_connected_to_output(bmain, snode->edittree, link->tonode)); - - snode_update(snode, link->tonode); - bNode *to_node = link->tonode; - nodeRemLink(snode->edittree, link); - sort_multi_input_socket_links(snode, to_node, NULL, NULL); - } - } - - if (found) { - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - snode_notify(C, snode); - if (do_tag_update) { - snode_dag_update(C, snode); - } - - return OPERATOR_FINISHED; - } - - return OPERATOR_CANCELLED; - } - - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; -} - -void NODE_OT_links_cut(wmOperatorType *ot) -{ - ot->name = "Cut Links"; - ot->idname = "NODE_OT_links_cut"; - ot->description = "Use the mouse to cut (remove) some links"; - - ot->invoke = WM_gesture_lines_invoke; - ot->modal = WM_gesture_lines_modal; - ot->exec = cut_links_exec; - ot->cancel = WM_gesture_lines_cancel; - - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - PropertyRNA *prop; - prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - /* internal */ - RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX); -} - -/* ********************** Mute links operator ***************** */ - -static int mute_links_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - bool do_tag_update = false; - - int i = 0; - float mcoords[256][2]; - RNA_BEGIN (op->ptr, itemptr, "path") { - float loc[2]; - - RNA_float_get_array(&itemptr, "loc", loc); - UI_view2d_region_to_view( - ®ion->v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]); - i++; - if (i >= 256) { - break; - } - } - RNA_END; - - if (i > 1) { - ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - - /* Count intersected links and clear test flag. */ - int tot = 0; - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { - continue; - } - link->flag &= ~NODE_LINK_TEST; - if (node_links_intersect(link, mcoords, i)) { - tot++; - } - } - if (tot == 0) { - return OPERATOR_CANCELLED; - } - - /* Mute links. */ - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link) || (link->flag & NODE_LINK_TEST)) { - continue; - } - - if (node_links_intersect(link, mcoords, i)) { - do_tag_update |= (do_tag_update || - node_connected_to_output(bmain, snode->edittree, link->tonode)); - - snode_update(snode, link->tonode); - nodeMuteLinkToggle(snode->edittree, link); - } - } - - /* Clear remaining test flags. */ - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { - continue; - } - link->flag &= ~NODE_LINK_TEST; - } - - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - snode_notify(C, snode); - if (do_tag_update) { - snode_dag_update(C, snode); - } - - return OPERATOR_FINISHED; - } - - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; -} - -void NODE_OT_links_mute(wmOperatorType *ot) -{ - ot->name = "Mute Links"; - ot->idname = "NODE_OT_links_mute"; - ot->description = "Use the mouse to mute links"; - - ot->invoke = WM_gesture_lines_invoke; - ot->modal = WM_gesture_lines_modal; - ot->exec = mute_links_exec; - ot->cancel = WM_gesture_lines_cancel; - - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - PropertyRNA *prop; - prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - /* internal */ - RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX); -} - -/* ********************** Detach links operator ***************** */ - -static int detach_links_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->flag & SELECT) { - nodeInternalRelink(ntree, node); - } - } - - ntreeUpdateTree(CTX_data_main(C), ntree); - - snode_notify(C, snode); - snode_dag_update(C, snode); - - return OPERATOR_FINISHED; -} - -void NODE_OT_links_detach(wmOperatorType *ot) -{ - ot->name = "Detach Links"; - ot->idname = "NODE_OT_links_detach"; - ot->description = - "Remove all links to selected nodes, and try to connect neighbor nodes together"; - - ot->exec = detach_links_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Set Parent ******************* */ - -static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - bNode *frame = nodeGetActive(ntree); - if (!frame || frame->type != NODE_FRAME) { - return OPERATOR_CANCELLED; - } - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node == frame) { - continue; - } - if (node->flag & NODE_SELECT) { - nodeDetachNode(node); - nodeAttachNode(node, frame); - } - } - - ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_parent_set(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Make Parent"; - ot->description = "Attach selected nodes"; - ot->idname = "NODE_OT_parent_set"; - - /* api callbacks */ - ot->exec = node_parent_set_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Join Nodes ******************* */ - -/* tags for depth-first search */ -#define NODE_JOIN_DONE 1 -#define NODE_JOIN_IS_DESCENDANT 2 - -static void node_join_attach_recursive(bNode *node, bNode *frame) -{ - node->done |= NODE_JOIN_DONE; - - if (node == frame) { - node->done |= NODE_JOIN_IS_DESCENDANT; - } - else if (node->parent) { - /* call recursively */ - if (!(node->parent->done & NODE_JOIN_DONE)) { - node_join_attach_recursive(node->parent, frame); - } - - /* in any case: if the parent is a descendant, so is the child */ - if (node->parent->done & NODE_JOIN_IS_DESCENDANT) { - node->done |= NODE_JOIN_IS_DESCENDANT; - } - else if (node->flag & NODE_TEST) { - /* if parent is not an descendant of the frame, reattach the node */ - nodeDetachNode(node); - nodeAttachNode(node, frame); - node->done |= NODE_JOIN_IS_DESCENDANT; - } - } - else if (node->flag & NODE_TEST) { - nodeAttachNode(node, frame); - node->done |= NODE_JOIN_IS_DESCENDANT; - } -} - -static int node_join_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - - /* XXX save selection: node_add_node call below sets the new frame as single - * active+selected node */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->flag & NODE_SELECT) { - node->flag |= NODE_TEST; - } - else { - node->flag &= ~NODE_TEST; - } - } - - bNode *frame = node_add_node(C, NULL, NODE_FRAME, 0.0f, 0.0f); - - /* reset tags */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - node->done = 0; - } - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (!(node->done & NODE_JOIN_DONE)) { - node_join_attach_recursive(node, frame); - } - } - - /* restore selection */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->flag & NODE_TEST) { - node->flag |= NODE_SELECT; - } - } - - ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_join(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Join Nodes"; - ot->description = "Attach selected nodes to a new common frame"; - ot->idname = "NODE_OT_join"; - - /* api callbacks */ - ot->exec = node_join_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Attach ******************* */ - -static bNode *node_find_frame_to_attach(ARegion *region, - const bNodeTree *ntree, - const int mouse_xy[2]) -{ - /* convert mouse coordinates to v2d space */ - float cursor[2]; - UI_view2d_region_to_view(®ion->v2d, UNPACK2(mouse_xy), &cursor[0], &cursor[1]); - - LISTBASE_FOREACH_BACKWARD (bNode *, frame, &ntree->nodes) { - /* skip selected, those are the nodes we want to attach */ - if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) { - continue; - } - if (BLI_rctf_isect_pt_v(&frame->totr, cursor)) { - return frame; - } - } - - return NULL; -} - -static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - bNode *frame = node_find_frame_to_attach(region, ntree, event->mval); - - if (frame) { - LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree->nodes) { - if (node->flag & NODE_SELECT) { - if (node->parent == NULL) { - /* disallow moving a parent into its child */ - if (nodeAttachNodeCheck(frame, node) == false) { - /* attach all unparented nodes */ - nodeAttachNode(node, frame); - } - } - else { - /* attach nodes which share parent with the frame */ - bNode *parent; - for (parent = frame->parent; parent; parent = parent->parent) { - if (parent == node->parent) { - break; - } - } - - if (parent) { - /* disallow moving a parent into its child */ - if (nodeAttachNodeCheck(frame, node) == false) { - nodeDetachNode(node); - nodeAttachNode(node, frame); - } - } - } - } - } - } - - ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_attach(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Attach Nodes"; - ot->description = "Attach active node to a frame"; - ot->idname = "NODE_OT_attach"; - - /* api callbacks */ - - ot->invoke = node_attach_invoke; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****************** Detach ******************* */ - -/* tags for depth-first search */ -#define NODE_DETACH_DONE 1 -#define NODE_DETACH_IS_DESCENDANT 2 - -static void node_detach_recursive(bNode *node) -{ - node->done |= NODE_DETACH_DONE; - - if (node->parent) { - /* call recursively */ - if (!(node->parent->done & NODE_DETACH_DONE)) { - node_detach_recursive(node->parent); - } - - /* in any case: if the parent is a descendant, so is the child */ - if (node->parent->done & NODE_DETACH_IS_DESCENDANT) { - node->done |= NODE_DETACH_IS_DESCENDANT; - } - else if (node->flag & NODE_SELECT) { - /* if parent is not a descendant of a selected node, detach */ - nodeDetachNode(node); - node->done |= NODE_DETACH_IS_DESCENDANT; - } - } - else if (node->flag & NODE_SELECT) { - node->done |= NODE_DETACH_IS_DESCENDANT; - } -} - -/* detach the root nodes in the current selection */ -static int node_detach_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - - /* reset tags */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - node->done = 0; - } - /* detach nodes recursively - * relative order is preserved here! - */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (!(node->done & NODE_DETACH_DONE)) { - node_detach_recursive(node); - } - } - - ED_node_sort(ntree); - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_detach(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Detach Nodes"; - ot->description = "Detach selected nodes from parents"; - ot->idname = "NODE_OT_detach"; - - /* api callbacks */ - ot->exec = node_detach_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ********************* automatic node insert on dragging ******************* */ - -/* prevent duplicate testing code below */ -static bool ed_node_link_conditions(ScrArea *area, - bool test, - SpaceNode **r_snode, - bNode **r_select) -{ - SpaceNode *snode = area ? area->spacedata.first : NULL; - - *r_snode = snode; - *r_select = NULL; - - /* no unlucky accidents */ - if (area == NULL || area->spacetype != SPACE_NODE) { - return false; - } - - if (!test) { - /* no need to look for a node */ - return true; - } - - bNode *node; - bNode *select = NULL; - for (node = snode->edittree->nodes.first; node; node = node->next) { - if (node->flag & SELECT) { - if (select) { - break; - } - select = node; - } - } - /* only one selected */ - if (node || select == NULL) { - return false; - } - - /* correct node */ - if (BLI_listbase_is_empty(&select->inputs) || BLI_listbase_is_empty(&select->outputs)) { - return false; - } - - /* test node for links */ - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { - continue; - } - - if (link->tonode == select || link->fromnode == select) { - return false; - } - } - - *r_select = select; - return true; -} - -/* test == 0, clear all intersect flags */ -void ED_node_link_intersect_test(ScrArea *area, int test) -{ - bNode *select; - SpaceNode *snode; - if (!ed_node_link_conditions(area, test, &snode, &select)) { - return; - } - - /* clear flags */ - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - link->flag &= ~NODE_LINKFLAG_HILITE; - } - - if (test == 0) { - return; - } - - /* find link to select/highlight */ - bNodeLink *selink = NULL; - float dist_best = FLT_MAX; - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - float coord_array[NODE_LINK_RESOL + 1][2]; - - if (nodeLinkIsHidden(link)) { - continue; - } - - if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) { - float dist = FLT_MAX; - - /* loop over link coords to find shortest dist to - * upper left node edge of a intersected line segment */ - for (int i = 0; i < NODE_LINK_RESOL; i++) { - /* Check if the node rectangle intersects the line from this point to next one. */ - if (BLI_rctf_isect_segment(&select->totr, coord_array[i], coord_array[i + 1])) { - /* store the shortest distance to the upper left edge - * of all intersections found so far */ - const float node_xy[] = {select->totr.xmin, select->totr.ymax}; - - /* to be precise coord_array should be clipped by select->totr, - * but not done since there's no real noticeable difference */ - dist = min_ff( - dist_squared_to_line_segment_v2(node_xy, coord_array[i], coord_array[i + 1]), dist); - } - } - - /* we want the link with the shortest distance to node center */ - if (dist < dist_best) { - dist_best = dist; - selink = link; - } - } - } - - if (selink) { - selink->flag |= NODE_LINKFLAG_HILITE; - } -} - -static int get_main_socket_priority(const bNodeSocket *socket) -{ - switch ((eNodeSocketDatatype)socket->type) { - case __SOCK_MESH: - case SOCK_CUSTOM: - return -1; - case SOCK_BOOLEAN: - return 0; - case SOCK_INT: - return 1; - case SOCK_FLOAT: - return 2; - case SOCK_VECTOR: - return 3; - case SOCK_RGBA: - return 4; - case SOCK_STRING: - case SOCK_SHADER: - case SOCK_OBJECT: - case SOCK_IMAGE: - case SOCK_GEOMETRY: - case SOCK_COLLECTION: - case SOCK_TEXTURE: - case SOCK_MATERIAL: - return 5; - } - return -1; -} - -/** Get the "main" socket of a socket list using a heuristic based on socket types. */ -static bNodeSocket *get_main_socket(ListBase *sockets) -{ - /* find priority range */ - int maxpriority = -1; - LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { - if (sock->flag & SOCK_UNAVAIL) { - continue; - } - maxpriority = max_ii(get_main_socket_priority(sock), maxpriority); - } - - /* try all priorities, starting from 'highest' */ - for (int priority = maxpriority; priority >= 0; priority--) { - LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { - if (!nodeSocketIsHidden(sock) && priority == get_main_socket_priority(sock)) { - return sock; - } - } - } - - /* no visible sockets, unhide first of highest priority */ - for (int priority = maxpriority; priority >= 0; priority--) { - LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { - if (sock->flag & SOCK_UNAVAIL) { - continue; - } - if (priority == get_main_socket_priority(sock)) { - sock->flag &= ~SOCK_HIDDEN; - return sock; - } - } - } - - return NULL; -} - -static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata)) -{ - /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */ - parent->flag |= NODE_TEST; - - return true; -} - -static void node_offset_apply(bNode *node, const float offset_x) -{ - /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */ - if ((node->flag & NODE_TEST) == 0) { - node->anim_init_locx = node->locx; - node->anim_ofsx = (offset_x / UI_DPI_FAC); - node->flag |= NODE_TEST; - } -} - -static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, const float offset_x) -{ - node_offset_apply(parent, offset_x); - - /* Flag all children as offset to prevent them from being offset - * separately (they've already moved with the parent). */ - LISTBASE_FOREACH (bNode *, node, &data->ntree->nodes) { - if (nodeIsChildOf(parent, node)) { - /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */ - node->flag |= NODE_TEST; - } - } -} - -#define NODE_INSOFS_ANIM_DURATION 0.25f - -/** - * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, similar - * to node_link_insert_offset_output_chain_cb below, but with slightly different logic - */ -static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode, - bNode *tonode, - void *userdata, - const bool reversed) -{ - NodeInsertOfsData *data = userdata; - bNode *ofs_node = reversed ? fromnode : tonode; - - if (ofs_node->parent && ofs_node->parent != data->insert_parent) { - node_offset_apply(ofs_node->parent, data->offset_x); - } - else { - node_offset_apply(ofs_node, data->offset_x); - } - - return true; -} - -/** - * Applies #NodeInsertOfsData.offset_x to all children of \a parent. - */ -static void node_link_insert_offset_frame_chains(const bNodeTree *ntree, - const bNode *parent, - NodeInsertOfsData *data, - const bool reversed) -{ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (nodeIsChildOf(parent, node)) { - nodeChainIter(ntree, node, node_link_insert_offset_frame_chain_cb, data, reversed); - } - } -} - -/** - * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, - * considering the logic needed for offsetting nodes after link insert - */ -static bool node_link_insert_offset_chain_cb(bNode *fromnode, - bNode *tonode, - void *userdata, - const bool reversed) -{ - NodeInsertOfsData *data = userdata; - bNode *ofs_node = reversed ? fromnode : tonode; - - if (data->insert_parent) { - if (ofs_node->parent && (ofs_node->parent->flag & NODE_TEST) == 0) { - node_parent_offset_apply(data, ofs_node->parent, data->offset_x); - node_link_insert_offset_frame_chains(data->ntree, ofs_node->parent, data, reversed); - } - else { - node_offset_apply(ofs_node, data->offset_x); - } - - if (nodeIsChildOf(data->insert_parent, ofs_node) == false) { - data->insert_parent = NULL; - } - } - else if (ofs_node->parent) { - bNode *node = nodeFindRootParent(ofs_node); - node_offset_apply(node, data->offset_x); - } - else { - node_offset_apply(ofs_node, data->offset_x); - } - - return true; -} - -static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, - ARegion *region, - const int mouse_xy[2], - const bool right_alignment) -{ - bNodeTree *ntree = iofsd->ntree; - bNode *insert = iofsd->insert; - bNode *prev = iofsd->prev, *next = iofsd->next; - bNode *init_parent = insert->parent; /* store old insert->parent for restoring later */ - - const float min_margin = U.node_margin * UI_DPI_FAC; - const float width = NODE_WIDTH(insert); - const bool needs_alignment = (next->totr.xmin - prev->totr.xmax) < (width + (min_margin * 2.0f)); - - float margin = width; - - /* NODE_TEST will be used later, so disable for all nodes */ - ntreeNodeFlagSet(ntree, NODE_TEST, false); - - /* insert->totr isn't updated yet, - * so totr_insert is used to get the correct worldspace coords */ - rctf totr_insert; - node_to_updated_rect(insert, &totr_insert); - - /* frame attachment wasn't handled yet - * so we search the frame that the node will be attached to later */ - insert->parent = node_find_frame_to_attach(region, ntree, mouse_xy); - - /* this makes sure nodes are also correctly offset when inserting a node on top of a frame - * without actually making it a part of the frame (because mouse isn't intersecting it) - * - logic here is similar to node_find_frame_to_attach */ - if (!insert->parent || - (prev->parent && (prev->parent == next->parent) && (prev->parent != insert->parent))) { - bNode *frame; - rctf totr_frame; - - /* check nodes front to back */ - for (frame = ntree->nodes.last; frame; frame = frame->prev) { - /* skip selected, those are the nodes we want to attach */ - if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) { - continue; - } - - /* for some reason frame y coords aren't correct yet */ - node_to_updated_rect(frame, &totr_frame); - - if (BLI_rctf_isect_x(&totr_frame, totr_insert.xmin) && - BLI_rctf_isect_x(&totr_frame, totr_insert.xmax)) { - if (BLI_rctf_isect_y(&totr_frame, totr_insert.ymin) || - BLI_rctf_isect_y(&totr_frame, totr_insert.ymax)) { - /* frame isn't insert->parent actually, but this is needed to make offsetting - * nodes work correctly for above checked cases (it is restored later) */ - insert->parent = frame; - break; - } - } - } - } - - /* *** ensure offset at the left (or right for right_alignment case) of insert_node *** */ - - float dist = right_alignment ? totr_insert.xmin - prev->totr.xmax : - next->totr.xmin - totr_insert.xmax; - /* distance between insert_node and prev is smaller than min margin */ - if (dist < min_margin) { - const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f); - - node_offset_apply(insert, addval); - - totr_insert.xmin += addval; - totr_insert.xmax += addval; - margin += min_margin; - } - - /* *** ensure offset at the right (or left for right_alignment case) of insert_node *** */ - - dist = right_alignment ? next->totr.xmin - totr_insert.xmax : totr_insert.xmin - prev->totr.xmax; - /* distance between insert_node and next is smaller than min margin */ - if (dist < min_margin) { - const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f); - if (needs_alignment) { - bNode *offs_node = right_alignment ? next : prev; - if (!offs_node->parent || offs_node->parent == insert->parent || - nodeIsChildOf(offs_node->parent, insert)) { - node_offset_apply(offs_node, addval); - } - else if (!insert->parent && offs_node->parent) { - node_offset_apply(nodeFindRootParent(offs_node), addval); - } - margin = addval; - } - /* enough room is available, but we want to ensure the min margin at the right */ - else { - /* offset inserted node so that min margin is kept at the right */ - node_offset_apply(insert, -addval); - } - } - - if (needs_alignment) { - iofsd->insert_parent = insert->parent; - iofsd->offset_x = margin; - - /* flag all parents of insert as offset to prevent them from being offset */ - nodeParentsIter(insert, node_parents_offset_flag_enable_cb, NULL); - /* iterate over entire chain and apply offsets */ - nodeChainIter(ntree, - right_alignment ? next : prev, - node_link_insert_offset_chain_cb, - iofsd, - !right_alignment); - } - - insert->parent = init_parent; -} - -/** - * Modal handler for insert offset animation - */ -static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) -{ - SpaceNode *snode = CTX_wm_space_node(C); - NodeInsertOfsData *iofsd = snode->runtime->iofsd; - bool redraw = false; - - if (!snode || event->type != TIMER || iofsd == NULL || iofsd->anim_timer != event->customdata) { - return OPERATOR_PASS_THROUGH; - } - - const float duration = (float)iofsd->anim_timer->duration; - - /* handle animation - do this before possibly aborting due to duration, since - * main thread might be so busy that node hasn't reached final position yet */ - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (UNLIKELY(node->anim_ofsx)) { - const float endval = node->anim_init_locx + node->anim_ofsx; - if (IS_EQF(node->locx, endval) == false) { - node->locx = BLI_easing_cubic_ease_in_out( - duration, node->anim_init_locx, node->anim_ofsx, NODE_INSOFS_ANIM_DURATION); - if (node->anim_ofsx < 0) { - CLAMP_MIN(node->locx, endval); - } - else { - CLAMP_MAX(node->locx, endval); - } - redraw = true; - } - } - } - if (redraw) { - ED_region_tag_redraw(CTX_wm_region(C)); - } - - /* end timer + free insert offset data */ - if (duration > NODE_INSOFS_ANIM_DURATION) { - WM_event_remove_timer(CTX_wm_manager(C), NULL, iofsd->anim_timer); - - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - node->anim_init_locx = node->anim_ofsx = 0.0f; - } - - snode->runtime->iofsd = NULL; - MEM_freeN(iofsd); - - return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); - } - - return OPERATOR_RUNNING_MODAL; -} - -#undef NODE_INSOFS_ANIM_DURATION - -static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - const SpaceNode *snode = CTX_wm_space_node(C); - NodeInsertOfsData *iofsd = snode->runtime->iofsd; - - if (!iofsd || !iofsd->insert) { - return OPERATOR_CANCELLED; - } - - BLI_assert((snode->flag & SNODE_SKIP_INSOFFSET) == 0); - - iofsd->ntree = snode->edittree; - iofsd->anim_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.02); - - node_link_insert_offset_ntree( - iofsd, CTX_wm_region(C), event->mval, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT)); - - /* add temp handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -void NODE_OT_insert_offset(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Insert Offset"; - ot->description = "Automatically offset nodes on insertion"; - ot->idname = "NODE_OT_insert_offset"; - - /* callbacks */ - ot->invoke = node_insert_offset_invoke; - ot->modal = node_insert_offset_modal; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; -} - -/* assumes link with NODE_LINKFLAG_HILITE set */ -void ED_node_link_insert(Main *bmain, ScrArea *area) -{ - bNode *select; - SpaceNode *snode; - if (!ed_node_link_conditions(area, true, &snode, &select)) { - return; - } - - /* get the link */ - bNodeLink *link; - for (link = snode->edittree->links.first; link; link = link->next) { - if (link->flag & NODE_LINKFLAG_HILITE) { - break; - } - } - - if (link) { - bNodeSocket *best_input = get_main_socket(&select->inputs); - bNodeSocket *best_output = get_main_socket(&select->outputs); - - if (best_input && best_output) { - bNode *node = link->tonode; - bNodeSocket *sockto = link->tosock; - - link->tonode = select; - link->tosock = best_input; - node_remove_extra_links(snode, link); - link->flag &= ~NODE_LINKFLAG_HILITE; - - bNodeLink *new_link = nodeAddLink(snode->edittree, select, best_output, node, sockto); - - /* Copy the socket index for the new link, and reset it for the old link. This way the - * relative order of links is preserved, and the links get drawn in the right place. */ - new_link->multi_input_socket_index = link->multi_input_socket_index; - link->multi_input_socket_index = 0; - - /* set up insert offset data, it needs stuff from here */ - if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) { - NodeInsertOfsData *iofsd = MEM_callocN(sizeof(NodeInsertOfsData), __func__); - - iofsd->insert = select; - iofsd->prev = link->fromnode; - iofsd->next = node; - - snode->runtime->iofsd = iofsd; - } - - ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */ - snode_update(snode, select); - ED_node_tag_update_id((ID *)snode->edittree); - ED_node_tag_update_id(snode->id); - } - } -} diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc new file mode 100644 index 00000000000..212724dc39a --- /dev/null +++ b/source/blender/editors/space_node/node_relationships.cc @@ -0,0 +1,2321 @@ +/* + * 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. + */ + +/** \file + * \ingroup spnode + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_anim_types.h" +#include "DNA_node_types.h" + +#include "BLI_blenlib.h" +#include "BLI_easing.h" +#include "BLI_math.h" + +#include "BKE_anim_data.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_node.h" + +#include "ED_node.h" /* own include */ +#include "ED_render.h" +#include "ED_screen.h" +#include "ED_util.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "BLT_translation.h" + +#include "node_intern.h" /* own include */ + +/* ****************** Relations helpers *********************** */ + +static bool ntree_has_drivers(bNodeTree *ntree) +{ + const AnimData *adt = BKE_animdata_from_id(&ntree->id); + if (adt == nullptr) { + return false; + } + return !BLI_listbase_is_empty(&adt->drivers); +} + +static bool ntree_check_nodes_connected_dfs(bNodeTree *ntree, bNode *from, bNode *to) +{ + if (from->flag & NODE_TEST) { + return false; + } + from->flag |= NODE_TEST; + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->fromnode == from) { + if (link->tonode == to) { + return true; + } + + if (ntree_check_nodes_connected_dfs(ntree, link->tonode, to)) { + return true; + } + } + } + return false; +} + +static bool ntree_check_nodes_connected(bNodeTree *ntree, bNode *from, bNode *to) +{ + if (from == to) { + return true; + } + ntreeNodeFlagSet(ntree, NODE_TEST, false); + return ntree_check_nodes_connected_dfs(ntree, from, to); +} + +static bool node_group_has_output_dfs(bNode *node) +{ + bNodeTree *ntree = (bNodeTree *)node->id; + if (ntree->id.tag & LIB_TAG_DOIT) { + return false; + } + ntree->id.tag |= LIB_TAG_DOIT; + for (bNode *current_node = (bNode *)ntree->nodes.first; current_node != nullptr; + current_node = current_node->next) { + if (current_node->type == NODE_GROUP) { + if (current_node->id && node_group_has_output_dfs(current_node)) { + return true; + } + } + if (current_node->flag & NODE_DO_OUTPUT && current_node->type != NODE_GROUP_OUTPUT) { + return true; + } + } + return false; +} + +static bool node_group_has_output(Main *bmain, bNode *node) +{ + BLI_assert(ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)); + bNodeTree *ntree = (bNodeTree *)node->id; + if (ntree == nullptr) { + return false; + } + BKE_main_id_tag_listbase(&bmain->nodetrees, LIB_TAG_DOIT, false); + return node_group_has_output_dfs(node); +} + +bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node) +{ + /* Special case for drivers: if node tree has any drivers we assume it is + * always to be tagged for update when node changes. Otherwise we will be + * doomed to do some deep and nasty deep search of indirect dependencies, + * which will be too complicated without real benefit. + */ + if (ntree_has_drivers(ntree)) { + return true; + } + LISTBASE_FOREACH (bNode *, current_node, &ntree->nodes) { + /* Special case for group nodes -- if modified node connected to a group + * with active output inside we consider refresh is needed. + * + * We could make check more grained here by taking which socket the node + * is connected to and so eventually. + */ + if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { + if (current_node->id != nullptr && ntree_has_drivers((bNodeTree *)current_node->id)) { + return true; + } + if (ntree_check_nodes_connected(ntree, node, current_node) && + node_group_has_output(bmain, current_node)) { + return true; + } + } + if (current_node->flag & NODE_DO_OUTPUT) { + if (ntree_check_nodes_connected(ntree, node, current_node)) { + return true; + } + } + } + return false; +} + +/* ****************** Add *********************** */ + +struct bNodeListItem { + struct bNodeListItem *next, *prev; + struct bNode *node; +}; + +struct NodeInsertOfsData { + bNodeTree *ntree; + bNode *insert; /* inserted node */ + bNode *prev, *next; /* prev/next node in the chain */ + bNode *insert_parent; + + wmTimer *anim_timer; + + float offset_x; /* offset to apply to node chain */ +}; + +static void clear_picking_highlight(ListBase *links) +{ + LISTBASE_FOREACH (bNodeLink *, link, links) { + link->flag &= ~NODE_LINK_TEMP_HIGHLIGHT; + } +} + +static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bNodeSocket *sock) +{ + LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data"); + bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link"); + linkdata->data = oplink; + if (sock->in_out == SOCK_OUT) { + oplink->fromnode = node; + oplink->fromsock = sock; + } + else { + oplink->tonode = node; + oplink->tosock = sock; + } + oplink->flag |= NODE_LINK_VALID; + oplink->flag &= ~NODE_LINK_TEST; + if (node_connected_to_output(bmain, snode->edittree, node)) { + oplink->flag |= NODE_LINK_TEST; + } + return linkdata; +} + +static void pick_link(const bContext *C, + wmOperator *op, + bNodeLinkDrag *nldrag, + SpaceNode *snode, + bNode *node, + bNodeLink *link_to_pick) +{ + clear_picking_highlight(&snode->edittree->links); + RNA_boolean_set(op->ptr, "has_link_picked", true); + + Main *bmain = CTX_data_main(C); + LinkData *linkdata = create_drag_link( + bmain, snode, link_to_pick->fromnode, link_to_pick->fromsock); + + BLI_addtail(&nldrag->links, linkdata); + nodeRemLink(snode->edittree, link_to_pick); + + BLI_assert(nldrag->last_node_hovered_while_dragging_a_link != nullptr); + + sort_multi_input_socket_links( + snode, nldrag->last_node_hovered_while_dragging_a_link, nullptr, nullptr); + + /* Send changed event to original link->tonode. */ + if (node) { + snode_update(snode, node); + } +} + +static void pick_input_link_by_link_intersect(const bContext *C, + wmOperator *op, + bNodeLinkDrag *nldrag, + const float *cursor) +{ + SpaceNode *snode = CTX_wm_space_node(C); + const ARegion *region = CTX_wm_region(C); + const View2D *v2d = ®ion->v2d; + + float drag_start[2]; + RNA_float_get_array(op->ptr, "drag_start", drag_start); + bNode *node; + bNodeSocket *socket; + node_find_indicated_socket(snode, &node, &socket, drag_start, SOCK_IN); + + /* Distance to test overlapping of cursor on link. */ + const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC; + + const int resolution = NODE_LINK_RESOL; + + bNodeLink *link_to_pick = nullptr; + clear_picking_highlight(&snode->edittree->links); + LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { + if (link->tosock == socket) { + /* Test if the cursor is near a link. */ + float vec[4][2]; + node_link_bezier_handles(v2d, snode, link, vec); + + float data[NODE_LINK_RESOL * 2 + 2]; + BKE_curve_forward_diff_bezier( + vec[0][0], vec[1][0], vec[2][0], vec[3][0], data, resolution, sizeof(float[2])); + BKE_curve_forward_diff_bezier( + vec[0][1], vec[1][1], vec[2][1], vec[3][1], data + 1, resolution, sizeof(float[2])); + + for (int i = 0; i < resolution * 2; i += 2) { + float *l1 = &data[i]; + float *l2 = &data[i + 2]; + float distance = dist_squared_to_line_segment_v2(cursor, l1, l2); + if (distance < cursor_link_touch_distance) { + link_to_pick = link; + nldrag->last_picked_multi_input_socket_link = link_to_pick; + } + } + } + } + + /* If no linked was picked in this call, try using the one picked in the previous call. + * Not essential for the basic behavior, but can make interaction feel a bit better if + * the mouse moves to the right and loses the "selection." */ + if (!link_to_pick) { + bNodeLink *last_picked_link = nldrag->last_picked_multi_input_socket_link; + if (last_picked_link) { + link_to_pick = last_picked_link; + } + } + + if (link_to_pick) { + /* Highlight is set here and cleared in the next iteration or if the operation finishes. */ + link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT; + ED_area_tag_redraw(CTX_wm_area(C)); + + if (!node_find_indicated_socket(snode, &node, &socket, cursor, SOCK_IN)) { + pick_link(C, op, nldrag, snode, node, link_to_pick); + } + } +} + +static int sort_nodes_locx(const void *a, const void *b) +{ + const bNodeListItem *nli1 = (const bNodeListItem *)a; + const bNodeListItem *nli2 = (const bNodeListItem *)b; + const bNode *node1 = nli1->node; + const bNode *node2 = nli2->node; + + if (node1->locx > node2->locx) { + return 1; + } + return 0; +} + +static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used) +{ + if (nodeSocketIsHidden(sock)) { + return false; + } + + if (!allow_used && (sock->flag & SOCK_IN_USE)) { + return false; + } + + return true; +} + +static bNodeSocket *best_socket_output(bNodeTree *ntree, + bNode *node, + bNodeSocket *sock_target, + const bool allow_multiple) +{ + /* first look for selected output */ + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + if (!socket_is_available(ntree, sock, allow_multiple)) { + continue; + } + + if (sock->flag & SELECT) { + return sock; + } + } + + /* try to find a socket with a matching name */ + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + if (!socket_is_available(ntree, sock, allow_multiple)) { + continue; + } + + /* check for same types */ + if (sock->type == sock_target->type) { + if (STREQ(sock->name, sock_target->name)) { + return sock; + } + } + } + + /* otherwise settle for the first available socket of the right type */ + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + if (!socket_is_available(ntree, sock, allow_multiple)) { + continue; + } + + /* check for same types */ + if (sock->type == sock_target->type) { + return sock; + } + } + + /* Always allow linking to an reroute node. The socket type of the reroute sockets might change + * after the link has been created. */ + if (node->type == NODE_REROUTE) { + return (bNodeSocket *)node->outputs.first; + } + + return nullptr; +} + +/* this is a bit complicated, but designed to prioritize finding + * sockets of higher types, such as image, first */ +static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace) +{ + int maxtype = 0; + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + maxtype = max_ii(sock->type, maxtype); + } + + /* find sockets of higher 'types' first (i.e. image) */ + int a = 0; + for (int socktype = maxtype; socktype >= 0; socktype--) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (!socket_is_available(ntree, sock, replace)) { + a++; + continue; + } + + if (sock->type == socktype) { + /* increment to make sure we don't keep finding + * the same socket on every attempt running this function */ + a++; + if (a > num) { + return sock; + } + } + } + } + + return nullptr; +} + +static bool snode_autoconnect_input(SpaceNode *snode, + bNode *node_fr, + bNodeSocket *sock_fr, + bNode *node_to, + bNodeSocket *sock_to, + int replace) +{ + bNodeTree *ntree = snode->edittree; + + /* then we can connect */ + if (replace) { + nodeRemSocketLinks(ntree, sock_to); + } + + nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to); + return true; +} + +struct LinkAndPosition { + struct bNodeLink *link; + float multi_socket_position[2]; +}; + +static int compare_link_by_y_position(const void *a, const void *b) +{ + const LinkAndPosition *link_and_position_a = *(const LinkAndPosition **)a; + const LinkAndPosition *link_and_position_b = *(const LinkAndPosition **)b; + + BLI_assert(link_and_position_a->link->tosock == link_and_position_b->link->tosock); + const float link_a_y = link_and_position_a->multi_socket_position[1]; + const float link_b_y = link_and_position_b->multi_socket_position[1]; + return link_a_y > link_b_y ? 1 : -1; +} + +void sort_multi_input_socket_links(SpaceNode *snode, + bNode *node, + bNodeLink *drag_link, + float cursor[2]) +{ + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (!(socket->flag & SOCK_MULTI_INPUT)) { + continue; + } + /* The total is calculated in #node_update_nodetree, which runs before this draw step. */ + int total_inputs = socket->total_inputs + 1; + struct LinkAndPosition **input_links = (LinkAndPosition **)MEM_malloc_arrayN( + total_inputs, sizeof(LinkAndPosition *), __func__); + + int index = 0; + LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { + if (link->tosock == socket) { + struct LinkAndPosition *link_and_position = (LinkAndPosition *)MEM_callocN( + sizeof(struct LinkAndPosition), __func__); + link_and_position->link = link; + node_link_calculate_multi_input_position(link->tosock->locx, + link->tosock->locy, + link->multi_input_socket_index, + link->tosock->total_inputs, + link_and_position->multi_socket_position); + input_links[index] = link_and_position; + index++; + } + } + + if (drag_link) { + LinkAndPosition *link_and_position = (LinkAndPosition *)MEM_callocN(sizeof(LinkAndPosition), + __func__); + link_and_position->link = drag_link; + copy_v2_v2(link_and_position->multi_socket_position, cursor); + input_links[index] = link_and_position; + index++; + } + + qsort(input_links, index, sizeof(bNodeLink *), compare_link_by_y_position); + + for (int i = 0; i < index; i++) { + input_links[i]->link->multi_input_socket_index = i; + } + + for (int i = 0; i < index; i++) { + if (input_links[i]) { + MEM_freeN(input_links[i]); + } + } + MEM_freeN(input_links); + } +} + +static void snode_autoconnect(Main *bmain, + SpaceNode *snode, + const bool allow_multiple, + const bool replace) +{ + bNodeTree *ntree = snode->edittree; + ListBase *nodelist = (ListBase *)MEM_callocN(sizeof(ListBase), "items_list"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->flag & NODE_SELECT) { + bNodeListItem *nli = (bNodeListItem *)MEM_mallocN(sizeof(bNodeListItem), + "temporary node list item"); + nli->node = node; + BLI_addtail(nodelist, nli); + } + } + + /* sort nodes left to right */ + BLI_listbase_sort(nodelist, sort_nodes_locx); + + int numlinks = 0; + LISTBASE_FOREACH (bNodeListItem *, nli, nodelist) { + bool has_selected_inputs = false; + + if (nli->next == nullptr) { + break; + } + + bNode *node_fr = nli->node; + bNode *node_to = nli->next->node; + /* corner case: input/output node aligned the wrong way around (T47729) */ + if (BLI_listbase_is_empty(&node_to->inputs) || BLI_listbase_is_empty(&node_fr->outputs)) { + SWAP(bNode *, node_fr, node_to); + } + + /* if there are selected sockets, connect those */ + LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) { + if (sock_to->flag & SELECT) { + has_selected_inputs = true; + + if (!socket_is_available(ntree, sock_to, replace)) { + continue; + } + + /* check for an appropriate output socket to connect from */ + bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple); + if (!sock_fr) { + continue; + } + + if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) { + numlinks++; + } + } + } + + if (!has_selected_inputs) { + /* no selected inputs, connect by finding suitable match */ + int num_inputs = BLI_listbase_count(&node_to->inputs); + + for (int i = 0; i < num_inputs; i++) { + + /* find the best guess input socket */ + bNodeSocket *sock_to = best_socket_input(ntree, node_to, i, replace); + if (!sock_to) { + continue; + } + + /* check for an appropriate output socket to connect from */ + bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple); + if (!sock_fr) { + continue; + } + + if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) { + numlinks++; + break; + } + } + } + } + + if (numlinks > 0) { + ntreeUpdateTree(bmain, ntree); + } + + BLI_freelistN(nodelist); + MEM_freeN(nodelist); +} + +/* *************************** link viewer op ******************** */ + +static int node_link_viewer(const bContext *C, bNode *tonode) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + /* context check */ + if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) { + return OPERATOR_CANCELLED; + } + if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { + return OPERATOR_CANCELLED; + } + + /* get viewer */ + bNode *viewer_node = nullptr; + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { + if (node->flag & NODE_DO_OUTPUT) { + viewer_node = node; + break; + } + } + } + /* no viewer, we make one active */ + if (viewer_node == nullptr) { + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { + node->flag |= NODE_DO_OUTPUT; + viewer_node = node; + break; + } + } + } + + bNodeSocket *sock = nullptr; + bNodeLink *link = nullptr; + + /* try to find an already connected socket to cycle to the next */ + if (viewer_node) { + link = nullptr; + + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { + if (link->tonode == viewer_node && link->fromnode == tonode) { + if (link->tosock == viewer_node->inputs.first) { + break; + } + } + } + if (link) { + /* unlink existing connection */ + sock = link->fromsock; + nodeRemLink(snode->edittree, link); + + /* find a socket after the previously connected socket */ + for (sock = sock->next; sock; sock = sock->next) { + if (!nodeSocketIsHidden(sock)) { + break; + } + } + } + } + + if (tonode) { + /* Find a selected socket that overrides the socket to connect to */ + LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { + if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { + sock = sock2; + break; + } + } + } + + /* find a socket starting from the first socket */ + if (!sock) { + for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { + if (!nodeSocketIsHidden(sock)) { + break; + } + } + } + + if (sock) { + /* add a new viewer if none exists yet */ + if (!viewer_node) { + /* XXX location is a quick hack, just place it next to the linked socket */ + viewer_node = node_add_node(C, nullptr, CMP_NODE_VIEWER, sock->locx + 100, sock->locy); + if (!viewer_node) { + return OPERATOR_CANCELLED; + } + + link = nullptr; + } + else { + /* get link to viewer */ + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { + if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) { + break; + } + } + } + + if (link == nullptr) { + nodeAddLink( + snode->edittree, tonode, sock, viewer_node, (bNodeSocket *)viewer_node->inputs.first); + } + else { + link->fromnode = tonode; + link->fromsock = sock; + /* make sure the dependency sorting is updated */ + snode->edittree->update |= NTREE_UPDATE_LINKS; + } + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + snode_update(snode, viewer_node); + } + + return OPERATOR_FINISHED; +} + +static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node = nodeGetActive(snode->edittree); + + if (!node) { + return OPERATOR_CANCELLED; + } + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + if (node_link_viewer(C, node) == OPERATOR_CANCELLED) { + return OPERATOR_CANCELLED; + } + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_link_viewer(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Link to Viewer Node"; + ot->description = "Link to viewer node"; + ot->idname = "NODE_OT_link_viewer"; + + /* api callbacks */ + ot->exec = node_active_link_viewer_exec; + ot->poll = composite_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* *************************** add link op ******************** */ + +static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag)) +{ + char header[UI_MAX_DRAW_STR]; + + BLI_strncpy(header, TIP_("LMB: drag node link, RMB: cancel"), sizeof(header)); + ED_workspace_status_text(C, header); +} + +static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket) +{ + int count = 0; + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (ELEM(socket, link->fromsock, link->tosock)) { + count++; + } + } + return count; +} + +static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link) +{ + bNodeTree *ntree = snode->edittree; + bNodeSocket *from = link->fromsock, *to = link->tosock; + int to_count = node_count_links(ntree, to); + int from_count = node_count_links(ntree, from); + int to_link_limit = nodeSocketLinkLimit(to); + int from_link_limit = nodeSocketLinkLimit(from); + + LISTBASE_FOREACH_MUTABLE (bNodeLink *, tlink, &ntree->links) { + if (tlink == link) { + continue; + } + + if (tlink && tlink->fromsock == from) { + if (from_count > from_link_limit) { + nodeRemLink(ntree, tlink); + tlink = nullptr; + from_count--; + } + } + + if (tlink && tlink->tosock == to) { + if (to_count > to_link_limit) { + nodeRemLink(ntree, tlink); + tlink = nullptr; + to_count--; + } + else if (tlink->fromsock == from) { + /* Also remove link if it comes from the same output. */ + nodeRemLink(ntree, tlink); + tlink = nullptr; + to_count--; + from_count--; + } + } + } +} + +static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata; + bool do_tag_update = false; + + /* avoid updates while applying links */ + ntree->is_updating = true; + LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { + bNodeLink *link = (bNodeLink *)linkdata->data; + + /* See note below, but basically TEST flag means that the link + * was connected to output (or to a node which affects the + * output). + */ + do_tag_update |= (link->flag & NODE_LINK_TEST) != 0; + + if (apply_links && link->tosock && link->fromsock) { + /* before actually adding the link, + * let nodes perform special link insertion handling + */ + if (link->fromnode->typeinfo->insert_link) { + link->fromnode->typeinfo->insert_link(ntree, link->fromnode, link); + } + if (link->tonode->typeinfo->insert_link) { + link->tonode->typeinfo->insert_link(ntree, link->tonode, link); + } + + /* add link to the node tree */ + BLI_addtail(&ntree->links, link); + + ntree->update |= NTREE_UPDATE_LINKS; + + /* tag tonode for update */ + link->tonode->update |= NODE_UPDATE; + + /* we might need to remove a link */ + node_remove_extra_links(snode, link); + + if (link->tonode) { + do_tag_update |= (do_tag_update || node_connected_to_output(bmain, ntree, link->tonode)); + } + } + else { + nodeRemLink(ntree, link); + } + } + ntree->is_updating = false; + + ntreeUpdateTree(bmain, ntree); + snode_notify(C, snode); + if (do_tag_update) { + snode_dag_update(C, snode); + } + + BLI_remlink(&snode->runtime->linkdrag, nldrag); + /* links->data pointers are either held by the tree or freed already */ + BLI_freelistN(&nldrag->links); + MEM_freeN(nldrag); +} + +static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2]) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata; + + if (nldrag->in_out == SOCK_OUT) { + bNode *tnode; + bNodeSocket *tsock = nullptr; + if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) { + LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { + bNodeLink *link = (bNodeLink *)linkdata->data; + + /* skip if socket is on the same node as the fromsock */ + if (tnode && link->fromnode == tnode) { + continue; + } + + /* Skip if tsock is already linked with this output. */ + bNodeLink *existing_link_connected_to_fromsock = nullptr; + LISTBASE_FOREACH (bNodeLink *, existing_link, &snode->edittree->links) { + if (existing_link->fromsock == link->fromsock && existing_link->tosock == tsock) { + existing_link_connected_to_fromsock = existing_link; + break; + } + } + + /* attach links to the socket */ + link->tonode = tnode; + link->tosock = tsock; + nldrag->last_node_hovered_while_dragging_a_link = tnode; + if (existing_link_connected_to_fromsock) { + link->multi_input_socket_index = + existing_link_connected_to_fromsock->multi_input_socket_index; + continue; + } + if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) { + sort_multi_input_socket_links(snode, tnode, link, cursor); + } + } + } + else { + LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { + bNodeLink *link = (bNodeLink *)linkdata->data; + if (nldrag->last_node_hovered_while_dragging_a_link) { + sort_multi_input_socket_links( + snode, nldrag->last_node_hovered_while_dragging_a_link, nullptr, cursor); + } + link->tonode = nullptr; + link->tosock = nullptr; + } + } + } + else { + bNode *tnode; + bNodeSocket *tsock = nullptr; + if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) { + LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { + bNodeLink *link = (bNodeLink *)linkdata->data; + + /* skip if this is already the target socket */ + if (link->fromsock == tsock) { + continue; + } + /* skip if socket is on the same node as the fromsock */ + if (tnode && link->tonode == tnode) { + continue; + } + + /* attach links to the socket */ + link->fromnode = tnode; + link->fromsock = tsock; + } + } + else { + LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { + bNodeLink *link = (bNodeLink *)linkdata->data; + + link->fromnode = nullptr; + link->fromsock = nullptr; + } + } + } +} + +/* Loop that adds a node-link, called by function below. */ +/* in_out = starting socket */ +static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata; + ARegion *region = CTX_wm_region(C); + float cursor[2]; + + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); + + switch (event->type) { + case MOUSEMOVE: + if (nldrag->from_multi_input_socket && !RNA_boolean_get(op->ptr, "has_link_picked")) { + pick_input_link_by_link_intersect(C, op, nldrag, cursor); + } + else { + node_link_find_socket(C, op, cursor); + + node_link_update_header(C, nldrag); + ED_region_tag_redraw(region); + } + break; + + case LEFTMOUSE: + case RIGHTMOUSE: + case MIDDLEMOUSE: { + if (event->val == KM_RELEASE) { + node_link_exit(C, op, true); + + ED_workspace_status_text(C, nullptr); + ED_region_tag_redraw(region); + SpaceNode *snode = CTX_wm_space_node(C); + clear_picking_highlight(&snode->edittree->links); + return OPERATOR_FINISHED; + } + break; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +/* return 1 when socket clicked */ +static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor[2], bool detach) +{ + bNodeLinkDrag *nldrag = nullptr; + + /* output indicated? */ + bNode *node; + bNodeSocket *sock; + if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) { + nldrag = (bNodeLinkDrag *)MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata"); + + const int num_links = nodeCountSocketLinks(snode->edittree, sock); + int link_limit = nodeSocketLinkLimit(sock); + if (num_links > 0 && (num_links >= link_limit || detach)) { + /* dragged links are fixed on input side */ + nldrag->in_out = SOCK_IN; + /* detach current links and store them in the operator data */ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { + if (link->fromsock == sock) { + LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data"); + bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link"); + linkdata->data = oplink; + *oplink = *link; + oplink->next = oplink->prev = nullptr; + oplink->flag |= NODE_LINK_VALID; + + /* The link could be disconnected and in that case we + * wouldn't be able to check whether tag update is + * needed or not when releasing mouse button. So we + * cache whether the link affects output or not here + * using TEST flag. + */ + oplink->flag &= ~NODE_LINK_TEST; + if (node_connected_to_output(bmain, snode->edittree, link->tonode)) { + oplink->flag |= NODE_LINK_TEST; + } + + BLI_addtail(&nldrag->links, linkdata); + nodeRemLink(snode->edittree, link); + } + } + } + else { + /* dragged links are fixed on output side */ + nldrag->in_out = SOCK_OUT; + /* create a new link */ + LinkData *linkdata = create_drag_link(bmain, snode, node, sock); + + BLI_addtail(&nldrag->links, linkdata); + } + } + /* or an input? */ + else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) { + nldrag = (bNodeLinkDrag *)MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata"); + nldrag->last_node_hovered_while_dragging_a_link = node; + + const int num_links = nodeCountSocketLinks(snode->edittree, sock); + if (num_links > 0) { + /* dragged links are fixed on output side */ + nldrag->in_out = SOCK_OUT; + /* detach current links and store them in the operator data */ + bNodeLink *link_to_pick; + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { + if (link->tosock == sock) { + if (sock->flag & SOCK_MULTI_INPUT) { + nldrag->from_multi_input_socket = true; + } + link_to_pick = link; + } + } + + if (link_to_pick != nullptr && !nldrag->from_multi_input_socket) { + LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data"); + bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link"); + linkdata->data = oplink; + *oplink = *link_to_pick; + oplink->next = oplink->prev = nullptr; + oplink->flag |= NODE_LINK_VALID; + oplink->flag &= ~NODE_LINK_TEST; + if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) { + oplink->flag |= NODE_LINK_TEST; + } + + BLI_addtail(&nldrag->links, linkdata); + nodeRemLink(snode->edittree, link_to_pick); + + /* send changed event to original link->tonode */ + if (node) { + snode_update(snode, node); + } + } + } + else { + /* dragged links are fixed on input side */ + nldrag->in_out = SOCK_IN; + /* create a new link */ + LinkData *linkdata = create_drag_link(bmain, snode, node, sock); + + BLI_addtail(&nldrag->links, linkdata); + } + } + + return nldrag; +} + +static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + + bool detach = RNA_boolean_get(op->ptr, "detach"); + + float cursor[2]; + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); + RNA_float_set_array(op->ptr, "drag_start", cursor); + RNA_boolean_set(op->ptr, "has_link_picked", false); + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + bNodeLinkDrag *nldrag = node_link_init(bmain, snode, cursor, detach); + + if (nldrag) { + op->customdata = nldrag; + BLI_addtail(&snode->runtime->linkdrag, nldrag); + + /* add modal handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; +} + +static void node_link_cancel(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata; + + BLI_remlink(&snode->runtime->linkdrag, nldrag); + + BLI_freelistN(&nldrag->links); + MEM_freeN(nldrag); + clear_picking_highlight(&snode->edittree->links); +} + +void NODE_OT_link(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Link Nodes"; + ot->idname = "NODE_OT_link"; + ot->description = "Use the mouse to create a link between two nodes"; + + /* api callbacks */ + ot->invoke = node_link_invoke; + ot->modal = node_link_modal; + // ot->exec = node_link_exec; + ot->poll = ED_operator_node_editable; + ot->cancel = node_link_cancel; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + PropertyRNA *prop; + + RNA_def_boolean(ot->srna, "detach", false, "Detach", "Detach and redirect existing links"); + prop = RNA_def_boolean( + ot->srna, + "has_link_picked", + false, + "Has Link Picked", + "The operation has placed a link. Only used for multi-input sockets, where the " + "link is picked later"); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_float_array(ot->srna, + "drag_start", + 2, + nullptr, + -UI_PRECISION_FLOAT_MAX, + UI_PRECISION_FLOAT_MAX, + "Drag Start", + "The position of the mouse cursor at the start of the operation", + -UI_PRECISION_FLOAT_MAX, + UI_PRECISION_FLOAT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_property_flag(prop, PROP_HIDDEN); +} + +/* ********************** Make Link operator ***************** */ + +/* makes a link between selected output and input sockets */ +static int node_make_link_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + const bool replace = RNA_boolean_get(op->ptr, "replace"); + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + snode_autoconnect(bmain, snode, true, replace); + + /* deselect sockets after linking */ + node_deselect_all_input_sockets(snode, false); + node_deselect_all_output_sockets(snode, false); + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_link_make(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Make Links"; + ot->description = "Makes a link between selected output in input sockets"; + ot->idname = "NODE_OT_link_make"; + + /* callbacks */ + ot->exec = node_make_link_exec; + /* XXX we need a special poll which checks that there are selected input/output sockets. */ + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, "replace", false, "Replace", "Replace socket connections with the new links"); +} + +/* ********************** Node Link Intersect ***************** */ +static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int tot) +{ + float coord_array[NODE_LINK_RESOL + 1][2]; + + if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) { + for (int i = 0; i < tot - 1; i++) { + for (int b = 0; b < NODE_LINK_RESOL; b++) { + if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) { + return true; + } + } + } + } + return false; +} + +/* ********************** Cut Link operator ***************** */ +static int cut_links_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + bool do_tag_update = false; + + int i = 0; + float mcoords[256][2]; + RNA_BEGIN (op->ptr, itemptr, "path") { + float loc[2]; + + RNA_float_get_array(&itemptr, "loc", loc); + UI_view2d_region_to_view( + ®ion->v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]); + i++; + if (i >= 256) { + break; + } + } + RNA_END; + + if (i > 1) { + bool found = false; + + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { + if (nodeLinkIsHidden(link)) { + continue; + } + + if (node_links_intersect(link, mcoords, i)) { + + if (found == false) { + /* TODO(sergey): Why did we kill jobs twice? */ + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + found = true; + } + + do_tag_update |= (do_tag_update || + node_connected_to_output(bmain, snode->edittree, link->tonode)); + + snode_update(snode, link->tonode); + bNode *to_node = link->tonode; + nodeRemLink(snode->edittree, link); + sort_multi_input_socket_links(snode, to_node, nullptr, nullptr); + } + } + + if (found) { + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + snode_notify(C, snode); + if (do_tag_update) { + snode_dag_update(C, snode); + } + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; + } + + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; +} + +void NODE_OT_links_cut(wmOperatorType *ot) +{ + ot->name = "Cut Links"; + ot->idname = "NODE_OT_links_cut"; + ot->description = "Use the mouse to cut (remove) some links"; + + ot->invoke = WM_gesture_lines_invoke; + ot->modal = WM_gesture_lines_modal; + ot->exec = cut_links_exec; + ot->cancel = WM_gesture_lines_cancel; + + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + + /* internal */ + RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX); +} + +/* ********************** Mute links operator ***************** */ + +static int mute_links_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + bool do_tag_update = false; + + int i = 0; + float mcoords[256][2]; + RNA_BEGIN (op->ptr, itemptr, "path") { + float loc[2]; + + RNA_float_get_array(&itemptr, "loc", loc); + UI_view2d_region_to_view( + ®ion->v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]); + i++; + if (i >= 256) { + break; + } + } + RNA_END; + + if (i > 1) { + ED_preview_kill_jobs(CTX_wm_manager(C), bmain); + + /* Count intersected links and clear test flag. */ + int tot = 0; + LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { + if (nodeLinkIsHidden(link)) { + continue; + } + link->flag &= ~NODE_LINK_TEST; + if (node_links_intersect(link, mcoords, i)) { + tot++; + } + } + if (tot == 0) { + return OPERATOR_CANCELLED; + } + + /* Mute links. */ + LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { + if (nodeLinkIsHidden(link) || (link->flag & NODE_LINK_TEST)) { + continue; + } + + if (node_links_intersect(link, mcoords, i)) { + do_tag_update |= (do_tag_update || + node_connected_to_output(bmain, snode->edittree, link->tonode)); + + snode_update(snode, link->tonode); + nodeMuteLinkToggle(snode->edittree, link); + } + } + + /* Clear remaining test flags. */ + LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { + if (nodeLinkIsHidden(link)) { + continue; + } + link->flag &= ~NODE_LINK_TEST; + } + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + snode_notify(C, snode); + if (do_tag_update) { + snode_dag_update(C, snode); + } + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; +} + +void NODE_OT_links_mute(wmOperatorType *ot) +{ + ot->name = "Mute Links"; + ot->idname = "NODE_OT_links_mute"; + ot->description = "Use the mouse to mute links"; + + ot->invoke = WM_gesture_lines_invoke; + ot->modal = WM_gesture_lines_modal; + ot->exec = mute_links_exec; + ot->cancel = WM_gesture_lines_cancel; + + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + + /* internal */ + RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX); +} + +/* ********************** Detach links operator ***************** */ + +static int detach_links_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->flag & SELECT) { + nodeInternalRelink(ntree, node); + } + } + + ntreeUpdateTree(CTX_data_main(C), ntree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_links_detach(wmOperatorType *ot) +{ + ot->name = "Detach Links"; + ot->idname = "NODE_OT_links_detach"; + ot->description = + "Remove all links to selected nodes, and try to connect neighbor nodes together"; + + ot->exec = detach_links_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Set Parent ******************* */ + +static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *frame = nodeGetActive(ntree); + if (!frame || frame->type != NODE_FRAME) { + return OPERATOR_CANCELLED; + } + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node == frame) { + continue; + } + if (node->flag & NODE_SELECT) { + nodeDetachNode(node); + nodeAttachNode(node, frame); + } + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_parent_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Make Parent"; + ot->description = "Attach selected nodes"; + ot->idname = "NODE_OT_parent_set"; + + /* api callbacks */ + ot->exec = node_parent_set_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Join Nodes ******************* */ + +/* tags for depth-first search */ +#define NODE_JOIN_DONE 1 +#define NODE_JOIN_IS_DESCENDANT 2 + +static void node_join_attach_recursive(bNode *node, bNode *frame) +{ + node->done |= NODE_JOIN_DONE; + + if (node == frame) { + node->done |= NODE_JOIN_IS_DESCENDANT; + } + else if (node->parent) { + /* call recursively */ + if (!(node->parent->done & NODE_JOIN_DONE)) { + node_join_attach_recursive(node->parent, frame); + } + + /* in any case: if the parent is a descendant, so is the child */ + if (node->parent->done & NODE_JOIN_IS_DESCENDANT) { + node->done |= NODE_JOIN_IS_DESCENDANT; + } + else if (node->flag & NODE_TEST) { + /* if parent is not an descendant of the frame, reattach the node */ + nodeDetachNode(node); + nodeAttachNode(node, frame); + node->done |= NODE_JOIN_IS_DESCENDANT; + } + } + else if (node->flag & NODE_TEST) { + nodeAttachNode(node, frame); + node->done |= NODE_JOIN_IS_DESCENDANT; + } +} + +static int node_join_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + + /* XXX save selection: node_add_node call below sets the new frame as single + * active+selected node */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->flag & NODE_SELECT) { + node->flag |= NODE_TEST; + } + else { + node->flag &= ~NODE_TEST; + } + } + + bNode *frame = node_add_node(C, nullptr, NODE_FRAME, 0.0f, 0.0f); + + /* reset tags */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + node->done = 0; + } + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (!(node->done & NODE_JOIN_DONE)) { + node_join_attach_recursive(node, frame); + } + } + + /* restore selection */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->flag & NODE_TEST) { + node->flag |= NODE_SELECT; + } + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_join(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Join Nodes"; + ot->description = "Attach selected nodes to a new common frame"; + ot->idname = "NODE_OT_join"; + + /* api callbacks */ + ot->exec = node_join_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Attach ******************* */ + +static bNode *node_find_frame_to_attach(ARegion *region, + const bNodeTree *ntree, + const int mouse_xy[2]) +{ + /* convert mouse coordinates to v2d space */ + float cursor[2]; + UI_view2d_region_to_view(®ion->v2d, UNPACK2(mouse_xy), &cursor[0], &cursor[1]); + + LISTBASE_FOREACH_BACKWARD (bNode *, frame, &ntree->nodes) { + /* skip selected, those are the nodes we want to attach */ + if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) { + continue; + } + if (BLI_rctf_isect_pt_v(&frame->totr, cursor)) { + return frame; + } + } + + return nullptr; +} + +static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + bNode *frame = node_find_frame_to_attach(region, ntree, event->mval); + + if (frame) { + LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree->nodes) { + if (node->flag & NODE_SELECT) { + if (node->parent == nullptr) { + /* disallow moving a parent into its child */ + if (nodeAttachNodeCheck(frame, node) == false) { + /* attach all unparented nodes */ + nodeAttachNode(node, frame); + } + } + else { + /* attach nodes which share parent with the frame */ + bNode *parent; + for (parent = frame->parent; parent; parent = parent->parent) { + if (parent == node->parent) { + break; + } + } + + if (parent) { + /* disallow moving a parent into its child */ + if (nodeAttachNodeCheck(frame, node) == false) { + nodeDetachNode(node); + nodeAttachNode(node, frame); + } + } + } + } + } + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_attach(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Attach Nodes"; + ot->description = "Attach active node to a frame"; + ot->idname = "NODE_OT_attach"; + + /* api callbacks */ + + ot->invoke = node_attach_invoke; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Detach ******************* */ + +/* tags for depth-first search */ +#define NODE_DETACH_DONE 1 +#define NODE_DETACH_IS_DESCENDANT 2 + +static void node_detach_recursive(bNode *node) +{ + node->done |= NODE_DETACH_DONE; + + if (node->parent) { + /* call recursively */ + if (!(node->parent->done & NODE_DETACH_DONE)) { + node_detach_recursive(node->parent); + } + + /* in any case: if the parent is a descendant, so is the child */ + if (node->parent->done & NODE_DETACH_IS_DESCENDANT) { + node->done |= NODE_DETACH_IS_DESCENDANT; + } + else if (node->flag & NODE_SELECT) { + /* if parent is not a descendant of a selected node, detach */ + nodeDetachNode(node); + node->done |= NODE_DETACH_IS_DESCENDANT; + } + } + else if (node->flag & NODE_SELECT) { + node->done |= NODE_DETACH_IS_DESCENDANT; + } +} + +/* detach the root nodes in the current selection */ +static int node_detach_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *ntree = snode->edittree; + + /* reset tags */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + node->done = 0; + } + /* detach nodes recursively + * relative order is preserved here! + */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (!(node->done & NODE_DETACH_DONE)) { + node_detach_recursive(node); + } + } + + ED_node_sort(ntree); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_detach(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Detach Nodes"; + ot->description = "Detach selected nodes from parents"; + ot->idname = "NODE_OT_detach"; + + /* api callbacks */ + ot->exec = node_detach_exec; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************* automatic node insert on dragging ******************* */ + +/* prevent duplicate testing code below */ +static bool ed_node_link_conditions(ScrArea *area, + bool test, + SpaceNode **r_snode, + bNode **r_select) +{ + SpaceNode *snode = area ? (SpaceNode *)area->spacedata.first : nullptr; + + *r_snode = snode; + *r_select = nullptr; + + /* no unlucky accidents */ + if (area == nullptr || area->spacetype != SPACE_NODE) { + return false; + } + + if (!test) { + /* no need to look for a node */ + return true; + } + + bNode *node; + bNode *select = nullptr; + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + if (node->flag & SELECT) { + if (select) { + break; + } + select = node; + } + } + /* only one selected */ + if (node || select == nullptr) { + return false; + } + + /* correct node */ + if (BLI_listbase_is_empty(&select->inputs) || BLI_listbase_is_empty(&select->outputs)) { + return false; + } + + /* test node for links */ + LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { + if (nodeLinkIsHidden(link)) { + continue; + } + + if (link->tonode == select || link->fromnode == select) { + return false; + } + } + + *r_select = select; + return true; +} + +/* test == 0, clear all intersect flags */ +void ED_node_link_intersect_test(ScrArea *area, int test) +{ + bNode *select; + SpaceNode *snode; + if (!ed_node_link_conditions(area, test, &snode, &select)) { + return; + } + + /* clear flags */ + LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { + link->flag &= ~NODE_LINKFLAG_HILITE; + } + + if (test == 0) { + return; + } + + /* find link to select/highlight */ + bNodeLink *selink = nullptr; + float dist_best = FLT_MAX; + LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { + float coord_array[NODE_LINK_RESOL + 1][2]; + + if (nodeLinkIsHidden(link)) { + continue; + } + + if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) { + float dist = FLT_MAX; + + /* loop over link coords to find shortest dist to + * upper left node edge of a intersected line segment */ + for (int i = 0; i < NODE_LINK_RESOL; i++) { + /* Check if the node rectangle intersects the line from this point to next one. */ + if (BLI_rctf_isect_segment(&select->totr, coord_array[i], coord_array[i + 1])) { + /* store the shortest distance to the upper left edge + * of all intersections found so far */ + const float node_xy[] = {select->totr.xmin, select->totr.ymax}; + + /* to be precise coord_array should be clipped by select->totr, + * but not done since there's no real noticeable difference */ + dist = min_ff( + dist_squared_to_line_segment_v2(node_xy, coord_array[i], coord_array[i + 1]), dist); + } + } + + /* we want the link with the shortest distance to node center */ + if (dist < dist_best) { + dist_best = dist; + selink = link; + } + } + } + + if (selink) { + selink->flag |= NODE_LINKFLAG_HILITE; + } +} + +static int get_main_socket_priority(const bNodeSocket *socket) +{ + switch ((eNodeSocketDatatype)socket->type) { + case __SOCK_MESH: + case SOCK_CUSTOM: + return -1; + case SOCK_BOOLEAN: + return 0; + case SOCK_INT: + return 1; + case SOCK_FLOAT: + return 2; + case SOCK_VECTOR: + return 3; + case SOCK_RGBA: + return 4; + case SOCK_STRING: + case SOCK_SHADER: + case SOCK_OBJECT: + case SOCK_IMAGE: + case SOCK_GEOMETRY: + case SOCK_COLLECTION: + case SOCK_TEXTURE: + case SOCK_MATERIAL: + return 5; + } + return -1; +} + +/** Get the "main" socket of a socket list using a heuristic based on socket types. */ +static bNodeSocket *get_main_socket(ListBase *sockets) +{ + /* find priority range */ + int maxpriority = -1; + LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { + if (sock->flag & SOCK_UNAVAIL) { + continue; + } + maxpriority = max_ii(get_main_socket_priority(sock), maxpriority); + } + + /* try all priorities, starting from 'highest' */ + for (int priority = maxpriority; priority >= 0; priority--) { + LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { + if (!nodeSocketIsHidden(sock) && priority == get_main_socket_priority(sock)) { + return sock; + } + } + } + + /* no visible sockets, unhide first of highest priority */ + for (int priority = maxpriority; priority >= 0; priority--) { + LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { + if (sock->flag & SOCK_UNAVAIL) { + continue; + } + if (priority == get_main_socket_priority(sock)) { + sock->flag &= ~SOCK_HIDDEN; + return sock; + } + } + } + + return nullptr; +} + +static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata)) +{ + /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */ + parent->flag |= NODE_TEST; + + return true; +} + +static void node_offset_apply(bNode *node, const float offset_x) +{ + /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */ + if ((node->flag & NODE_TEST) == 0) { + node->anim_init_locx = node->locx; + node->anim_ofsx = (offset_x / UI_DPI_FAC); + node->flag |= NODE_TEST; + } +} + +static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, const float offset_x) +{ + node_offset_apply(parent, offset_x); + + /* Flag all children as offset to prevent them from being offset + * separately (they've already moved with the parent). */ + LISTBASE_FOREACH (bNode *, node, &data->ntree->nodes) { + if (nodeIsChildOf(parent, node)) { + /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */ + node->flag |= NODE_TEST; + } + } +} + +#define NODE_INSOFS_ANIM_DURATION 0.25f + +/** + * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, similar + * to node_link_insert_offset_output_chain_cb below, but with slightly different logic + */ +static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode, + bNode *tonode, + void *userdata, + const bool reversed) +{ + NodeInsertOfsData *data = (NodeInsertOfsData *)userdata; + bNode *ofs_node = reversed ? fromnode : tonode; + + if (ofs_node->parent && ofs_node->parent != data->insert_parent) { + node_offset_apply(ofs_node->parent, data->offset_x); + } + else { + node_offset_apply(ofs_node, data->offset_x); + } + + return true; +} + +/** + * Applies #NodeInsertOfsData.offset_x to all children of \a parent. + */ +static void node_link_insert_offset_frame_chains(const bNodeTree *ntree, + const bNode *parent, + NodeInsertOfsData *data, + const bool reversed) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (nodeIsChildOf(parent, node)) { + nodeChainIter(ntree, node, node_link_insert_offset_frame_chain_cb, data, reversed); + } + } +} + +/** + * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, + * considering the logic needed for offsetting nodes after link insert + */ +static bool node_link_insert_offset_chain_cb(bNode *fromnode, + bNode *tonode, + void *userdata, + const bool reversed) +{ + NodeInsertOfsData *data = (NodeInsertOfsData *)userdata; + bNode *ofs_node = reversed ? fromnode : tonode; + + if (data->insert_parent) { + if (ofs_node->parent && (ofs_node->parent->flag & NODE_TEST) == 0) { + node_parent_offset_apply(data, ofs_node->parent, data->offset_x); + node_link_insert_offset_frame_chains(data->ntree, ofs_node->parent, data, reversed); + } + else { + node_offset_apply(ofs_node, data->offset_x); + } + + if (nodeIsChildOf(data->insert_parent, ofs_node) == false) { + data->insert_parent = nullptr; + } + } + else if (ofs_node->parent) { + bNode *node = nodeFindRootParent(ofs_node); + node_offset_apply(node, data->offset_x); + } + else { + node_offset_apply(ofs_node, data->offset_x); + } + + return true; +} + +static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, + ARegion *region, + const int mouse_xy[2], + const bool right_alignment) +{ + bNodeTree *ntree = iofsd->ntree; + bNode *insert = iofsd->insert; + bNode *prev = iofsd->prev, *next = iofsd->next; + bNode *init_parent = insert->parent; /* store old insert->parent for restoring later */ + + const float min_margin = U.node_margin * UI_DPI_FAC; + const float width = NODE_WIDTH(insert); + const bool needs_alignment = (next->totr.xmin - prev->totr.xmax) < (width + (min_margin * 2.0f)); + + float margin = width; + + /* NODE_TEST will be used later, so disable for all nodes */ + ntreeNodeFlagSet(ntree, NODE_TEST, false); + + /* insert->totr isn't updated yet, + * so totr_insert is used to get the correct worldspace coords */ + rctf totr_insert; + node_to_updated_rect(insert, &totr_insert); + + /* frame attachment wasn't handled yet + * so we search the frame that the node will be attached to later */ + insert->parent = node_find_frame_to_attach(region, ntree, mouse_xy); + + /* this makes sure nodes are also correctly offset when inserting a node on top of a frame + * without actually making it a part of the frame (because mouse isn't intersecting it) + * - logic here is similar to node_find_frame_to_attach */ + if (!insert->parent || + (prev->parent && (prev->parent == next->parent) && (prev->parent != insert->parent))) { + bNode *frame; + rctf totr_frame; + + /* check nodes front to back */ + for (frame = (bNode *)ntree->nodes.last; frame; frame = frame->prev) { + /* skip selected, those are the nodes we want to attach */ + if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) { + continue; + } + + /* for some reason frame y coords aren't correct yet */ + node_to_updated_rect(frame, &totr_frame); + + if (BLI_rctf_isect_x(&totr_frame, totr_insert.xmin) && + BLI_rctf_isect_x(&totr_frame, totr_insert.xmax)) { + if (BLI_rctf_isect_y(&totr_frame, totr_insert.ymin) || + BLI_rctf_isect_y(&totr_frame, totr_insert.ymax)) { + /* frame isn't insert->parent actually, but this is needed to make offsetting + * nodes work correctly for above checked cases (it is restored later) */ + insert->parent = frame; + break; + } + } + } + } + + /* *** ensure offset at the left (or right for right_alignment case) of insert_node *** */ + + float dist = right_alignment ? totr_insert.xmin - prev->totr.xmax : + next->totr.xmin - totr_insert.xmax; + /* distance between insert_node and prev is smaller than min margin */ + if (dist < min_margin) { + const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f); + + node_offset_apply(insert, addval); + + totr_insert.xmin += addval; + totr_insert.xmax += addval; + margin += min_margin; + } + + /* *** ensure offset at the right (or left for right_alignment case) of insert_node *** */ + + dist = right_alignment ? next->totr.xmin - totr_insert.xmax : totr_insert.xmin - prev->totr.xmax; + /* distance between insert_node and next is smaller than min margin */ + if (dist < min_margin) { + const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f); + if (needs_alignment) { + bNode *offs_node = right_alignment ? next : prev; + if (!offs_node->parent || offs_node->parent == insert->parent || + nodeIsChildOf(offs_node->parent, insert)) { + node_offset_apply(offs_node, addval); + } + else if (!insert->parent && offs_node->parent) { + node_offset_apply(nodeFindRootParent(offs_node), addval); + } + margin = addval; + } + /* enough room is available, but we want to ensure the min margin at the right */ + else { + /* offset inserted node so that min margin is kept at the right */ + node_offset_apply(insert, -addval); + } + } + + if (needs_alignment) { + iofsd->insert_parent = insert->parent; + iofsd->offset_x = margin; + + /* flag all parents of insert as offset to prevent them from being offset */ + nodeParentsIter(insert, node_parents_offset_flag_enable_cb, nullptr); + /* iterate over entire chain and apply offsets */ + nodeChainIter(ntree, + right_alignment ? next : prev, + node_link_insert_offset_chain_cb, + iofsd, + !right_alignment); + } + + insert->parent = init_parent; +} + +/** + * Modal handler for insert offset animation + */ +static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + SpaceNode *snode = CTX_wm_space_node(C); + NodeInsertOfsData *iofsd = snode->runtime->iofsd; + bool redraw = false; + + if (!snode || event->type != TIMER || iofsd == nullptr || + iofsd->anim_timer != event->customdata) { + return OPERATOR_PASS_THROUGH; + } + + const float duration = (float)iofsd->anim_timer->duration; + + /* handle animation - do this before possibly aborting due to duration, since + * main thread might be so busy that node hasn't reached final position yet */ + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + if (UNLIKELY(node->anim_ofsx)) { + const float endval = node->anim_init_locx + node->anim_ofsx; + if (IS_EQF(node->locx, endval) == false) { + node->locx = BLI_easing_cubic_ease_in_out( + duration, node->anim_init_locx, node->anim_ofsx, NODE_INSOFS_ANIM_DURATION); + if (node->anim_ofsx < 0) { + CLAMP_MIN(node->locx, endval); + } + else { + CLAMP_MAX(node->locx, endval); + } + redraw = true; + } + } + } + if (redraw) { + ED_region_tag_redraw(CTX_wm_region(C)); + } + + /* end timer + free insert offset data */ + if (duration > NODE_INSOFS_ANIM_DURATION) { + WM_event_remove_timer(CTX_wm_manager(C), nullptr, iofsd->anim_timer); + + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + node->anim_init_locx = node->anim_ofsx = 0.0f; + } + + snode->runtime->iofsd = nullptr; + MEM_freeN(iofsd); + + return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); + } + + return OPERATOR_RUNNING_MODAL; +} + +#undef NODE_INSOFS_ANIM_DURATION + +static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const SpaceNode *snode = CTX_wm_space_node(C); + NodeInsertOfsData *iofsd = snode->runtime->iofsd; + + if (!iofsd || !iofsd->insert) { + return OPERATOR_CANCELLED; + } + + BLI_assert((snode->flag & SNODE_SKIP_INSOFFSET) == 0); + + iofsd->ntree = snode->edittree; + iofsd->anim_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.02); + + node_link_insert_offset_ntree( + iofsd, CTX_wm_region(C), event->mval, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT)); + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void NODE_OT_insert_offset(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Insert Offset"; + ot->description = "Automatically offset nodes on insertion"; + ot->idname = "NODE_OT_insert_offset"; + + /* callbacks */ + ot->invoke = node_insert_offset_invoke; + ot->modal = node_insert_offset_modal; + ot->poll = ED_operator_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; +} + +/* assumes link with NODE_LINKFLAG_HILITE set */ +void ED_node_link_insert(Main *bmain, ScrArea *area) +{ + bNode *select; + SpaceNode *snode; + if (!ed_node_link_conditions(area, true, &snode, &select)) { + return; + } + + /* get the link */ + bNodeLink *link; + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { + if (link->flag & NODE_LINKFLAG_HILITE) { + break; + } + } + + if (link) { + bNodeSocket *best_input = get_main_socket(&select->inputs); + bNodeSocket *best_output = get_main_socket(&select->outputs); + + if (best_input && best_output) { + bNode *node = link->tonode; + bNodeSocket *sockto = link->tosock; + + link->tonode = select; + link->tosock = best_input; + node_remove_extra_links(snode, link); + link->flag &= ~NODE_LINKFLAG_HILITE; + + bNodeLink *new_link = nodeAddLink(snode->edittree, select, best_output, node, sockto); + + /* Copy the socket index for the new link, and reset it for the old link. This way the + * relative order of links is preserved, and the links get drawn in the right place. */ + new_link->multi_input_socket_index = link->multi_input_socket_index; + link->multi_input_socket_index = 0; + + /* set up insert offset data, it needs stuff from here */ + if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) { + NodeInsertOfsData *iofsd = (NodeInsertOfsData *)MEM_callocN(sizeof(NodeInsertOfsData), + __func__); + + iofsd->insert = select; + iofsd->prev = link->fromnode; + iofsd->next = node; + + snode->runtime->iofsd = iofsd; + } + + ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */ + snode_update(snode, select); + ED_node_tag_update_id((ID *)snode->edittree); + ED_node_tag_update_id(snode->id); + } + } +} diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c deleted file mode 100644 index de63aa07acb..00000000000 --- a/source/blender/editors/space_node/node_select.c +++ /dev/null @@ -1,1312 +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) 2008 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup spnode - */ - -#include - -#include "DNA_node_types.h" -#include "DNA_windowmanager_types.h" - -#include "BLI_lasso_2d.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_rect.h" -#include "BLI_string.h" -#include "BLI_string_search.h" -#include "BLI_string_utf8.h" -#include "BLI_utildefines.h" - -#include "BKE_context.h" -#include "BKE_main.h" -#include "BKE_node.h" -#include "BKE_workspace.h" - -#include "ED_node.h" /* own include */ -#include "ED_screen.h" -#include "ED_select_utils.h" -#include "ED_view3d.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "UI_interface.h" -#include "UI_resources.h" -#include "UI_view2d.h" - -#include "DEG_depsgraph.h" - -#include "MEM_guardedalloc.h" - -#include "node_intern.h" /* own include */ - -/** - * Function to detect if there is a visible view3d that uses workbench in texture mode. - * This function is for fixing T76970 for Blender 2.83. The actual fix should add a mechanism in - * the depsgraph that can be used by the draw engines to check if they need to be redrawn. - * - * We don't want to add these risky changes this close before releasing 2.83 without good testing - * hence this workaround. There are still cases were too many updates happen. For example when you - * have both a Cycles and workbench with textures viewport. - */ -static bool has_workbench_in_texture_color(const wmWindowManager *wm, - const Scene *scene, - const Object *ob) -{ - LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - if (win->scene != scene) { - continue; - } - const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (area->spacetype == SPACE_VIEW3D) { - const View3D *v3d = area->spacedata.first; - - if (ED_view3d_has_workbench_in_texture_color(scene, ob, v3d)) { - return true; - } - } - } - } - return false; -} - -/* -------------------------------------------------------------------- */ -/** \name Public Node Selection API - * \{ */ - -static bNode *node_under_mouse_select(bNodeTree *ntree, int mx, int my) -{ - bNode *node; - - for (node = ntree->nodes.last; node; node = node->prev) { - if (node->typeinfo->select_area_func) { - if (node->typeinfo->select_area_func(node, mx, my)) { - return node; - } - } - } - return NULL; -} - -static bNode *node_under_mouse_tweak(bNodeTree *ntree, int mx, int my) -{ - bNode *node; - - for (node = ntree->nodes.last; node; node = node->prev) { - if (node->typeinfo->tweak_area_func) { - if (node->typeinfo->tweak_area_func(node, mx, my)) { - return node; - } - } - } - return NULL; -} - -static bool is_position_over_node_or_socket(SpaceNode *snode, float mouse[2]) -{ - if (node_under_mouse_tweak(snode->edittree, mouse[0], mouse[1])) { - return true; - } - - bNode *node; - bNodeSocket *sock; - if (node_find_indicated_socket(snode, &node, &sock, mouse, SOCK_IN | SOCK_OUT)) { - return true; - } - - return false; -} - -static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - float mouse[2]; - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &mouse[0], &mouse[1]); - return is_position_over_node_or_socket(snode, mouse); -} - -static void node_toggle(bNode *node) -{ - nodeSetSelected(node, !(node->flag & SELECT)); -} - -void node_socket_select(bNode *node, bNodeSocket *sock) -{ - sock->flag |= SELECT; - - /* select node too */ - if (node) { - node->flag |= SELECT; - } -} - -void node_socket_deselect(bNode *node, bNodeSocket *sock, const bool deselect_node) -{ - sock->flag &= ~SELECT; - - if (node && deselect_node) { - bool sel = 0; - - /* if no selected sockets remain, also deselect the node */ - for (sock = node->inputs.first; sock; sock = sock->next) { - if (sock->flag & SELECT) { - sel = 1; - break; - } - } - for (sock = node->outputs.first; sock; sock = sock->next) { - if (sock->flag & SELECT) { - sel = 1; - break; - } - } - - if (!sel) { - node->flag &= ~SELECT; - } - } -} - -static void node_socket_toggle(bNode *node, bNodeSocket *sock, int deselect_node) -{ - if (sock->flag & SELECT) { - node_socket_deselect(node, sock, deselect_node); - } - else { - node_socket_select(node, sock); - } -} - -/* no undo here! */ -void node_deselect_all(SpaceNode *snode) -{ - bNode *node; - - for (node = snode->edittree->nodes.first; node; node = node->next) { - nodeSetSelected(node, false); - } -} - -void node_deselect_all_input_sockets(SpaceNode *snode, const bool deselect_nodes) -{ - bNode *node; - bNodeSocket *sock; - - /* XXX not calling node_socket_deselect here each time, because this does iteration - * over all node sockets internally to check if the node stays selected. - * We can do that more efficiently here. - */ - - for (node = snode->edittree->nodes.first; node; node = node->next) { - int sel = 0; - - for (sock = node->inputs.first; sock; sock = sock->next) { - sock->flag &= ~SELECT; - } - - /* if no selected sockets remain, also deselect the node */ - if (deselect_nodes) { - for (sock = node->outputs.first; sock; sock = sock->next) { - if (sock->flag & SELECT) { - sel = 1; - break; - } - } - - if (!sel) { - node->flag &= ~SELECT; - } - } - } -} - -void node_deselect_all_output_sockets(SpaceNode *snode, const bool deselect_nodes) -{ - bNode *node; - bNodeSocket *sock; - - /* XXX not calling node_socket_deselect here each time, because this does iteration - * over all node sockets internally to check if the node stays selected. - * We can do that more efficiently here. - */ - - for (node = snode->edittree->nodes.first; node; node = node->next) { - bool sel = false; - - for (sock = node->outputs.first; sock; sock = sock->next) { - sock->flag &= ~SELECT; - } - - /* if no selected sockets remain, also deselect the node */ - if (deselect_nodes) { - for (sock = node->inputs.first; sock; sock = sock->next) { - if (sock->flag & SELECT) { - sel = 1; - break; - } - } - - if (!sel) { - node->flag &= ~SELECT; - } - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Grouped Operator - * \{ */ - -/* Return true if we need redraw, otherwise false. */ - -static bool node_select_grouped_type(SpaceNode *snode, bNode *node_act) -{ - bNode *node; - bool changed = false; - - for (node = snode->edittree->nodes.first; node; node = node->next) { - if ((node->flag & SELECT) == 0) { - if (node->type == node_act->type) { - nodeSetSelected(node, true); - changed = true; - } - } - } - - return changed; -} - -static bool node_select_grouped_color(SpaceNode *snode, bNode *node_act) -{ - bNode *node; - bool changed = false; - - for (node = snode->edittree->nodes.first; node; node = node->next) { - if ((node->flag & SELECT) == 0) { - if (compare_v3v3(node->color, node_act->color, 0.005f)) { - nodeSetSelected(node, true); - changed = true; - } - } - } - - return changed; -} - -static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bool from_right) -{ - bNode *node; - bool changed = false; - const uint delims[] = {'.', '-', '_', '\0'}; - size_t pref_len_act, pref_len_curr; - const char *sep, *suf_act, *suf_curr; - - pref_len_act = BLI_str_partition_ex_utf8( - node_act->name, NULL, delims, &sep, &suf_act, from_right); - - /* Note: in case we are searching for suffix, and found none, use whole name as suffix. */ - if (from_right && !(sep && suf_act)) { - pref_len_act = 0; - suf_act = node_act->name; - } - - for (node = snode->edittree->nodes.first; node; node = node->next) { - if (node->flag & SELECT) { - continue; - } - pref_len_curr = BLI_str_partition_ex_utf8( - node->name, NULL, delims, &sep, &suf_curr, from_right); - - /* Same as with active node name! */ - if (from_right && !(sep && suf_curr)) { - pref_len_curr = 0; - suf_curr = node->name; - } - - if ((from_right && STREQ(suf_act, suf_curr)) || - (!from_right && (pref_len_act == pref_len_curr) && - STREQLEN(node_act->name, node->name, pref_len_act))) { - nodeSetSelected(node, true); - changed = true; - } - } - - return changed; -} - -enum { - NODE_SELECT_GROUPED_TYPE = 0, - NODE_SELECT_GROUPED_COLOR = 1, - NODE_SELECT_GROUPED_PREFIX = 2, - NODE_SELECT_GROUPED_SUFIX = 3, -}; - -static int node_select_grouped_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNode *node_act = nodeGetActive(snode->edittree); - - if (node_act == NULL) { - return OPERATOR_CANCELLED; - } - - bNode *node; - bool changed = false; - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const int type = RNA_enum_get(op->ptr, "type"); - - if (!extend) { - for (node = snode->edittree->nodes.first; node; node = node->next) { - nodeSetSelected(node, false); - } - } - nodeSetSelected(node_act, true); - - switch (type) { - case NODE_SELECT_GROUPED_TYPE: - changed = node_select_grouped_type(snode, node_act); - break; - case NODE_SELECT_GROUPED_COLOR: - changed = node_select_grouped_color(snode, node_act); - break; - case NODE_SELECT_GROUPED_PREFIX: - changed = node_select_grouped_name(snode, node_act, false); - break; - case NODE_SELECT_GROUPED_SUFIX: - changed = node_select_grouped_name(snode, node_act, true); - break; - default: - break; - } - - if (changed) { - ED_node_sort(snode->edittree); - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); - return OPERATOR_FINISHED; - } - - return OPERATOR_CANCELLED; -} - -void NODE_OT_select_grouped(wmOperatorType *ot) -{ - static const EnumPropertyItem prop_select_grouped_types[] = { - {NODE_SELECT_GROUPED_TYPE, "TYPE", 0, "Type", ""}, - {NODE_SELECT_GROUPED_COLOR, "COLOR", 0, "Color", ""}, - {NODE_SELECT_GROUPED_PREFIX, "PREFIX", 0, "Prefix", ""}, - {NODE_SELECT_GROUPED_SUFIX, "SUFFIX", 0, "Suffix", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Select Grouped"; - ot->description = "Select nodes with similar properties"; - ot->idname = "NODE_OT_select_grouped"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = node_select_grouped_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, - "extend", - false, - "Extend", - "Extend selection instead of deselecting everything first"); - ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select (Cursor Pick) Operator - * \{ */ - -void node_select_single(bContext *C, bNode *node) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - const Object *ob = CTX_data_active_object(C); - const Scene *scene = CTX_data_scene(C); - const wmWindowManager *wm = CTX_wm_manager(C); - bool active_texture_changed = false; - bNode *tnode; - - for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) { - if (tnode != node) { - nodeSetSelected(tnode, false); - } - } - nodeSetSelected(node, true); - - ED_node_set_active(bmain, snode->edittree, node, &active_texture_changed); - ED_node_set_active_viewer_key(snode); - - ED_node_sort(snode->edittree); - if (active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) { - DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE); - } - - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); -} - -static int node_mouse_select(bContext *C, - wmOperator *op, - const int mval[2], - bool wait_to_deselect_others) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - const Object *ob = CTX_data_active_object(C); - const Scene *scene = CTX_data_scene(C); - const wmWindowManager *wm = CTX_wm_manager(C); - bNode *node, *tnode; - bNodeSocket *sock = NULL; - bNodeSocket *tsock; - float cursor[2]; - int ret_value = OPERATOR_CANCELLED; - - const bool extend = RNA_boolean_get(op->ptr, "extend"); - /* always do socket_select when extending selection. */ - const bool socket_select = extend || RNA_boolean_get(op->ptr, "socket_select"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - - /* These cases are never modal. */ - if (extend || socket_select) { - wait_to_deselect_others = false; - } - - /* get mouse coordinates in view2d space */ - UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &cursor[0], &cursor[1]); - - /* first do socket selection, these generally overlap with nodes. */ - if (socket_select) { - if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) { - /* NOTE: SOCK_IN does not take into account the extend case... - * This feature is not really used anyway currently? */ - node_socket_toggle(node, sock, true); - ret_value = OPERATOR_FINISHED; - } - else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) { - if (sock->flag & SELECT) { - if (extend) { - node_socket_deselect(node, sock, true); - } - else { - ret_value = OPERATOR_FINISHED; - } - } - else { - /* Only allow one selected output per node, for sensible linking. - * Allow selecting outputs from different nodes though, if extend is true. */ - if (node) { - for (tsock = node->outputs.first; tsock; tsock = tsock->next) { - if (tsock == sock) { - continue; - } - node_socket_deselect(node, tsock, true); - } - } - if (!extend) { - for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) { - if (tnode == node) { - continue; - } - for (tsock = tnode->outputs.first; tsock; tsock = tsock->next) { - node_socket_deselect(tnode, tsock, true); - } - } - } - node_socket_select(node, sock); - ret_value = OPERATOR_FINISHED; - } - } - } - - if (!sock) { - /* find the closest visible node */ - node = node_under_mouse_select(snode->edittree, (int)cursor[0], (int)cursor[1]); - - if (extend) { - if (node != NULL) { - /* If node is selected but not active, we want to make it active, - * but not toggle (deselect) it. */ - if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) { - node_toggle(node); - } - ret_value = OPERATOR_FINISHED; - } - } - else if (deselect_all && node == NULL) { - /* Rather than deselecting others, users may want to drag to box-select (drag from empty - * space) or tweak-translate an already selected item. If these cases may apply, delay - * deselection. */ - if (wait_to_deselect_others) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else { - /* Deselect in empty space. */ - for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) { - nodeSetSelected(tnode, false); - } - ret_value = OPERATOR_FINISHED; - } - } - else if (node != NULL) { - /* When clicking on an already selected node, we want to wait to deselect - * others and allow the user to start moving the node without that. */ - if (wait_to_deselect_others && (node->flag & SELECT)) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else { - nodeSetSelected(node, true); - - for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) { - if (tnode != node) { - nodeSetSelected(tnode, false); - } - } - - ret_value = OPERATOR_FINISHED; - } - } - } - - /* update node order */ - if (ret_value != OPERATOR_CANCELLED) { - bool active_texture_changed = false; - if (node != NULL && ret_value != OPERATOR_RUNNING_MODAL) { - ED_node_set_active(bmain, snode->edittree, node, &active_texture_changed); - } - ED_node_set_active_viewer_key(snode); - ED_node_sort(snode->edittree); - if (active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) { - DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE); - } - - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); - } - - return ret_value; -} - -static int node_select_exec(bContext *C, wmOperator *op) -{ - const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); - - /* get settings from RNA properties for operator */ - int mval[2]; - mval[0] = RNA_int_get(op->ptr, "mouse_x"); - mval[1] = RNA_int_get(op->ptr, "mouse_y"); - - /* perform the select */ - const int ret_value = node_mouse_select(C, op, mval, wait_to_deselect_others); - - /* allow tweak event to work too */ - return ret_value | OPERATOR_PASS_THROUGH; -} - -void NODE_OT_select(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Select"; - ot->idname = "NODE_OT_select"; - ot->description = "Select the node under the cursor"; - - /* api callbacks */ - ot->exec = node_select_exec; - ot->invoke = WM_generic_select_invoke; - ot->modal = WM_generic_select_modal; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - WM_operator_properties_generic_select(ot); - RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); - RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); - 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); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Box Select Operator - * \{ */ - -static int node_box_select_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - rctf rectf; - - WM_operator_properties_border_to_rctf(op, &rectf); - UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); - - const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); - const bool select = (sel_op != SEL_OP_SUB); - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT); - } - - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - bool is_inside; - if (node->type == NODE_FRAME) { - is_inside = BLI_rctf_inside_rctf(&rectf, &node->totr); - } - else { - is_inside = BLI_rctf_isect(&rectf, &node->totr, NULL); - } - - if (is_inside) { - nodeSetSelected(node, select); - } - } - - ED_node_sort(snode->edittree); - - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); - - return OPERATOR_FINISHED; -} - -static int node_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - const bool tweak = RNA_boolean_get(op->ptr, "tweak"); - - if (tweak && is_event_over_node_or_socket(C, event)) { - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; - } - - return WM_gesture_box_invoke(C, op, event); -} - -void NODE_OT_select_box(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Box Select"; - ot->idname = "NODE_OT_select_box"; - ot->description = "Use box selection to select nodes"; - - /* api callbacks */ - ot->invoke = node_box_select_invoke; - ot->exec = node_box_select_exec; - ot->modal = WM_gesture_box_modal; - ot->cancel = WM_gesture_box_cancel; - - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, - "tweak", - 0, - "Tweak", - "Only activate when mouse is not over a node (useful for tweak gesture)"); - - WM_operator_properties_gesture_box(ot); - WM_operator_properties_select_operation_simple(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Circle Select Operator - * \{ */ - -static int node_circleselect_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - bNode *node; - - int x, y, radius; - float offset[2]; - - float zoom = (float)(BLI_rcti_size_x(®ion->winrct)) / - (float)(BLI_rctf_size_x(®ion->v2d.cur)); - - const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"), - WM_gesture_is_modal_first(op->customdata)); - const bool select = (sel_op != SEL_OP_SUB); - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT); - } - - /* get operator properties */ - x = RNA_int_get(op->ptr, "x"); - y = RNA_int_get(op->ptr, "y"); - radius = RNA_int_get(op->ptr, "radius"); - - UI_view2d_region_to_view(®ion->v2d, x, y, &offset[0], &offset[1]); - - for (node = snode->edittree->nodes.first; node; node = node->next) { - if (BLI_rctf_isect_circle(&node->totr, offset, radius / zoom)) { - nodeSetSelected(node, select); - } - } - - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_select_circle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Circle Select"; - ot->idname = "NODE_OT_select_circle"; - ot->description = "Use circle selection to select nodes"; - - /* api callbacks */ - ot->invoke = WM_gesture_circle_invoke; - ot->exec = node_circleselect_exec; - ot->modal = WM_gesture_circle_modal; - - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - WM_operator_properties_gesture_circle(ot); - WM_operator_properties_select_operation_simple(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Lasso Select Operator - * \{ */ - -static int node_lasso_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - const bool tweak = RNA_boolean_get(op->ptr, "tweak"); - - if (tweak && is_event_over_node_or_socket(C, event)) { - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; - } - - return WM_gesture_lasso_invoke(C, op, event); -} - -static bool do_lasso_select_node(bContext *C, - const int mcoords[][2], - const int mcoords_len, - eSelectOp sel_op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNode *node; - - ARegion *region = CTX_wm_region(C); - - rcti rect; - bool changed = false; - - const bool select = (sel_op != SEL_OP_SUB); - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT); - changed = true; - } - - /* get rectangle from operator */ - BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - - /* do actual selection */ - for (node = snode->edittree->nodes.first; node; node = node->next) { - - if (select && (node->flag & NODE_SELECT)) { - continue; - } - - int screen_co[2]; - const float cent[2] = {BLI_rctf_cent_x(&node->totr), BLI_rctf_cent_y(&node->totr)}; - - /* marker in screen coords */ - if (UI_view2d_view_to_region_clip( - ®ion->v2d, cent[0], cent[1], &screen_co[0], &screen_co[1]) && - BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) && - BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co[0], screen_co[1], INT_MAX)) { - nodeSetSelected(node, select); - changed = true; - } - } - - if (changed) { - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); - } - - return changed; -} - -static int node_lasso_select_exec(bContext *C, wmOperator *op) -{ - int mcoords_len; - const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); - - if (mcoords) { - const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); - - do_lasso_select_node(C, mcoords, mcoords_len, sel_op); - - MEM_freeN((void *)mcoords); - - return OPERATOR_FINISHED; - } - return OPERATOR_PASS_THROUGH; -} - -void NODE_OT_select_lasso(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Lasso Select"; - ot->description = "Select nodes using lasso selection"; - ot->idname = "NODE_OT_select_lasso"; - - /* api callbacks */ - ot->invoke = node_lasso_select_invoke; - ot->modal = WM_gesture_lasso_modal; - ot->exec = node_lasso_select_exec; - ot->poll = ED_operator_node_active; - ot->cancel = WM_gesture_lasso_cancel; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, - "tweak", - 0, - "Tweak", - "Only activate when mouse is not over a node (useful for tweak gesture)"); - - WM_operator_properties_gesture_lasso(ot); - WM_operator_properties_select_operation_simple(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name (De)select All Operator - * \{ */ - -static int node_select_all_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ListBase *node_lb = &snode->edittree->nodes; - int action = RNA_enum_get(op->ptr, "action"); - - ED_node_select_all(node_lb, action); - - ED_node_sort(snode->edittree); - - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); - return OPERATOR_FINISHED; -} - -void NODE_OT_select_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "(De)select All"; - ot->description = "(De)select all nodes"; - ot->idname = "NODE_OT_select_all"; - - /* api callbacks */ - ot->exec = node_select_all_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - WM_operator_properties_select_all(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Linked To Operator - * \{ */ - -static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeLink *link; - bNode *node; - - for (node = snode->edittree->nodes.first; node; node = node->next) { - node->flag &= ~NODE_TEST; - } - - for (link = snode->edittree->links.first; link; link = link->next) { - if (nodeLinkIsHidden(link)) { - continue; - } - if (link->fromnode && link->tonode && (link->fromnode->flag & NODE_SELECT)) { - link->tonode->flag |= NODE_TEST; - } - } - - for (node = snode->edittree->nodes.first; node; node = node->next) { - if (node->flag & NODE_TEST) { - nodeSetSelected(node, true); - } - } - - ED_node_sort(snode->edittree); - - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); - return OPERATOR_FINISHED; -} - -void NODE_OT_select_linked_to(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Linked To"; - ot->description = "Select nodes linked to the selected ones"; - ot->idname = "NODE_OT_select_linked_to"; - - /* api callbacks */ - ot->exec = node_select_linked_to_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Linked From Operator - * \{ */ - -static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeLink *link; - bNode *node; - - for (node = snode->edittree->nodes.first; node; node = node->next) { - node->flag &= ~NODE_TEST; - } - - for (link = snode->edittree->links.first; link; link = link->next) { - if (nodeLinkIsHidden(link)) { - continue; - } - if (link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT)) { - link->fromnode->flag |= NODE_TEST; - } - } - - for (node = snode->edittree->nodes.first; node; node = node->next) { - if (node->flag & NODE_TEST) { - nodeSetSelected(node, true); - } - } - - ED_node_sort(snode->edittree); - - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); - return OPERATOR_FINISHED; -} - -void NODE_OT_select_linked_from(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Linked From"; - ot->description = "Select nodes linked from the selected ones"; - ot->idname = "NODE_OT_select_linked_from"; - - /* api callbacks */ - ot->exec = node_select_linked_from_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Same Type Step Operator - * \{ */ - -static int node_select_same_type_step_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - bNode **node_array; - bNode *active = nodeGetActive(snode->edittree); - int totnodes; - const bool revert = RNA_boolean_get(op->ptr, "prev"); - const bool same_type = 1; - - ntreeGetDependencyList(snode->edittree, &node_array, &totnodes); - - if (totnodes > 1) { - int a; - - for (a = 0; a < totnodes; a++) { - if (node_array[a] == active) { - break; - } - } - - if (same_type) { - bNode *node = NULL; - - while (node == NULL) { - if (revert) { - a--; - } - else { - a++; - } - - if (a < 0 || a >= totnodes) { - break; - } - - node = node_array[a]; - - if (node->type == active->type) { - break; - } - node = NULL; - } - if (node) { - active = node; - } - } - else { - if (revert) { - if (a == 0) { - active = node_array[totnodes - 1]; - } - else { - active = node_array[a - 1]; - } - } - else { - if (a == totnodes - 1) { - active = node_array[0]; - } - else { - active = node_array[a + 1]; - } - } - } - - node_select_single(C, active); - - /* is note outside view? */ - if (active->totr.xmax < region->v2d.cur.xmin || active->totr.xmin > region->v2d.cur.xmax || - active->totr.ymax < region->v2d.cur.ymin || active->totr.ymin > region->v2d.cur.ymax) { - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - space_node_view_flag(C, snode, region, NODE_SELECT, smooth_viewtx); - } - } - - if (node_array) { - MEM_freeN(node_array); - } - - return OPERATOR_FINISHED; -} - -void NODE_OT_select_same_type_step(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Activate Same Type Next/Prev"; - ot->description = "Activate and view same node type, step by step"; - ot->idname = "NODE_OT_select_same_type_step"; - - /* api callbacks */ - ot->exec = node_select_same_type_step_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean(ot->srna, "prev", 0, "Previous", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Find Node by Name Operator - * \{ */ - -static void node_find_create_label(const bNode *node, char *str, int maxlen) -{ - if (node->label[0]) { - BLI_snprintf(str, maxlen, "%s (%s)", node->name, node->label); - } - else { - BLI_strncpy(str, node->name, maxlen); - } -} - -/* generic search invoke */ -static void node_find_update_fn(const struct bContext *C, - void *UNUSED(arg), - const char *str, - uiSearchItems *items, - const bool UNUSED(is_first)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - StringSearch *search = BLI_string_search_new(); - - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - char name[256]; - node_find_create_label(node, name, ARRAY_SIZE(name)); - BLI_string_search_add(search, name, node); - } - - bNode **filtered_nodes; - int filtered_amount = BLI_string_search_query(search, str, (void ***)&filtered_nodes); - - for (int i = 0; i < filtered_amount; i++) { - bNode *node = filtered_nodes[i]; - char name[256]; - node_find_create_label(node, name, ARRAY_SIZE(name)); - if (!UI_search_item_add(items, name, node, ICON_NONE, 0, 0)) { - break; - } - } - - MEM_freeN(filtered_nodes); - BLI_string_search_free(search); -} - -static void node_find_exec_fn(struct bContext *C, void *UNUSED(arg1), void *arg2) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNode *active = arg2; - - if (active) { - ARegion *region = CTX_wm_region(C); - node_select_single(C, active); - - /* is note outside view? */ - if (active->totr.xmax < region->v2d.cur.xmin || active->totr.xmin > region->v2d.cur.xmax || - active->totr.ymax < region->v2d.cur.ymin || active->totr.ymin > region->v2d.cur.ymax) { - space_node_view_flag(C, snode, region, NODE_SELECT, U.smooth_viewtx); - } - } -} - -static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op) -{ - static char search[256] = ""; - uiBlock *block; - uiBut *but; - wmOperator *op = (wmOperator *)arg_op; - - block = UI_block_begin(C, region, "_popup", UI_EMBOSS); - UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU); - UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); - - but = uiDefSearchBut(block, - search, - 0, - ICON_VIEWZOOM, - sizeof(search), - 10, - 10, - UI_searchbox_size_x(), - UI_UNIT_Y, - 0, - 0, - ""); - UI_but_func_search_set( - but, NULL, node_find_update_fn, op->type, false, NULL, node_find_exec_fn, NULL); - UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); - - /* fake button, it holds space for search items */ - uiDefBut(block, - UI_BTYPE_LABEL, - 0, - "", - 10, - 10 - UI_searchbox_size_y(), - UI_searchbox_size_x(), - UI_searchbox_size_y(), - NULL, - 0, - 0, - 0, - 0, - NULL); - - /* Move it downwards, mouse over button. */ - UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, (const int[2]){0, -UI_UNIT_Y}); - - return block; -} - -static int node_find_node_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - UI_popup_block_invoke(C, node_find_menu, op, NULL); - return OPERATOR_CANCELLED; -} - -void NODE_OT_find_node(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Find Node"; - ot->description = "Search for a node by name and focus and select it"; - ot->idname = "NODE_OT_find_node"; - - /* api callbacks */ - ot->invoke = node_find_node_invoke; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean(ot->srna, "prev", 0, "Previous", ""); -} - -/** \} */ diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc new file mode 100644 index 00000000000..41820cd813c --- /dev/null +++ b/source/blender/editors/space_node/node_select.cc @@ -0,0 +1,1315 @@ +/* + * 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) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spnode + */ + +#include +#include + +#include "DNA_node_types.h" +#include "DNA_windowmanager_types.h" + +#include "BLI_lasso_2d.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_rect.h" +#include "BLI_string.h" +#include "BLI_string_search.h" +#include "BLI_string_utf8.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_workspace.h" + +#include "ED_node.h" /* own include */ +#include "ED_screen.h" +#include "ED_select_utils.h" +#include "ED_view3d.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "DEG_depsgraph.h" + +#include "MEM_guardedalloc.h" + +#include "node_intern.h" /* own include */ + +/** + * Function to detect if there is a visible view3d that uses workbench in texture mode. + * This function is for fixing T76970 for Blender 2.83. The actual fix should add a mechanism in + * the depsgraph that can be used by the draw engines to check if they need to be redrawn. + * + * We don't want to add these risky changes this close before releasing 2.83 without good testing + * hence this workaround. There are still cases were too many updates happen. For example when you + * have both a Cycles and workbench with textures viewport. + */ +static bool has_workbench_in_texture_color(const wmWindowManager *wm, + const Scene *scene, + const Object *ob) +{ + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + if (win->scene != scene) { + continue; + } + const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_VIEW3D) { + const View3D *v3d = (const View3D *)area->spacedata.first; + + if (ED_view3d_has_workbench_in_texture_color(scene, ob, v3d)) { + return true; + } + } + } + } + return false; +} + +/* -------------------------------------------------------------------- */ +/** \name Public Node Selection API + * \{ */ + +static bNode *node_under_mouse_select(bNodeTree *ntree, int mx, int my) +{ + bNode *node; + + for (node = (bNode *)ntree->nodes.last; node; node = node->prev) { + if (node->typeinfo->select_area_func) { + if (node->typeinfo->select_area_func(node, mx, my)) { + return node; + } + } + } + return nullptr; +} + +static bNode *node_under_mouse_tweak(bNodeTree *ntree, int mx, int my) +{ + bNode *node; + + for (node = (bNode *)ntree->nodes.last; node; node = node->prev) { + if (node->typeinfo->tweak_area_func) { + if (node->typeinfo->tweak_area_func(node, mx, my)) { + return node; + } + } + } + return nullptr; +} + +static bool is_position_over_node_or_socket(SpaceNode *snode, float mouse[2]) +{ + if (node_under_mouse_tweak(snode->edittree, mouse[0], mouse[1])) { + return true; + } + + bNode *node; + bNodeSocket *sock; + if (node_find_indicated_socket(snode, &node, &sock, mouse, SOCK_IN | SOCK_OUT)) { + return true; + } + + return false; +} + +static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + float mouse[2]; + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &mouse[0], &mouse[1]); + return is_position_over_node_or_socket(snode, mouse); +} + +static void node_toggle(bNode *node) +{ + nodeSetSelected(node, !(node->flag & SELECT)); +} + +void node_socket_select(bNode *node, bNodeSocket *sock) +{ + sock->flag |= SELECT; + + /* select node too */ + if (node) { + node->flag |= SELECT; + } +} + +void node_socket_deselect(bNode *node, bNodeSocket *sock, const bool deselect_node) +{ + sock->flag &= ~SELECT; + + if (node && deselect_node) { + bool sel = false; + + /* if no selected sockets remain, also deselect the node */ + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { + if (sock->flag & SELECT) { + sel = true; + break; + } + } + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { + if (sock->flag & SELECT) { + sel = true; + break; + } + } + + if (!sel) { + node->flag &= ~SELECT; + } + } +} + +static void node_socket_toggle(bNode *node, bNodeSocket *sock, int deselect_node) +{ + if (sock->flag & SELECT) { + node_socket_deselect(node, sock, deselect_node); + } + else { + node_socket_select(node, sock); + } +} + +/* no undo here! */ +void node_deselect_all(SpaceNode *snode) +{ + bNode *node; + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + nodeSetSelected(node, false); + } +} + +void node_deselect_all_input_sockets(SpaceNode *snode, const bool deselect_nodes) +{ + bNode *node; + bNodeSocket *sock; + + /* XXX not calling node_socket_deselect here each time, because this does iteration + * over all node sockets internally to check if the node stays selected. + * We can do that more efficiently here. + */ + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + int sel = 0; + + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { + sock->flag &= ~SELECT; + } + + /* if no selected sockets remain, also deselect the node */ + if (deselect_nodes) { + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { + if (sock->flag & SELECT) { + sel = 1; + break; + } + } + + if (!sel) { + node->flag &= ~SELECT; + } + } + } +} + +void node_deselect_all_output_sockets(SpaceNode *snode, const bool deselect_nodes) +{ + bNode *node; + bNodeSocket *sock; + + /* XXX not calling node_socket_deselect here each time, because this does iteration + * over all node sockets internally to check if the node stays selected. + * We can do that more efficiently here. + */ + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + bool sel = false; + + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { + sock->flag &= ~SELECT; + } + + /* if no selected sockets remain, also deselect the node */ + if (deselect_nodes) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { + if (sock->flag & SELECT) { + sel = true; + break; + } + } + + if (!sel) { + node->flag &= ~SELECT; + } + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Grouped Operator + * \{ */ + +/* Return true if we need redraw, otherwise false. */ + +static bool node_select_grouped_type(SpaceNode *snode, bNode *node_act) +{ + bNode *node; + bool changed = false; + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + if ((node->flag & SELECT) == 0) { + if (node->type == node_act->type) { + nodeSetSelected(node, true); + changed = true; + } + } + } + + return changed; +} + +static bool node_select_grouped_color(SpaceNode *snode, bNode *node_act) +{ + bNode *node; + bool changed = false; + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + if ((node->flag & SELECT) == 0) { + if (compare_v3v3(node->color, node_act->color, 0.005f)) { + nodeSetSelected(node, true); + changed = true; + } + } + } + + return changed; +} + +static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bool from_right) +{ + bNode *node; + bool changed = false; + const uint delims[] = {'.', '-', '_', '\0'}; + size_t pref_len_act, pref_len_curr; + const char *sep, *suf_act, *suf_curr; + + pref_len_act = BLI_str_partition_ex_utf8( + node_act->name, nullptr, delims, &sep, &suf_act, from_right); + + /* Note: in case we are searching for suffix, and found none, use whole name as suffix. */ + if (from_right && !(sep && suf_act)) { + pref_len_act = 0; + suf_act = node_act->name; + } + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + if (node->flag & SELECT) { + continue; + } + pref_len_curr = BLI_str_partition_ex_utf8( + node->name, nullptr, delims, &sep, &suf_curr, from_right); + + /* Same as with active node name! */ + if (from_right && !(sep && suf_curr)) { + pref_len_curr = 0; + suf_curr = node->name; + } + + if ((from_right && STREQ(suf_act, suf_curr)) || + (!from_right && (pref_len_act == pref_len_curr) && + STREQLEN(node_act->name, node->name, pref_len_act))) { + nodeSetSelected(node, true); + changed = true; + } + } + + return changed; +} + +enum { + NODE_SELECT_GROUPED_TYPE = 0, + NODE_SELECT_GROUPED_COLOR = 1, + NODE_SELECT_GROUPED_PREFIX = 2, + NODE_SELECT_GROUPED_SUFIX = 3, +}; + +static int node_select_grouped_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node_act = nodeGetActive(snode->edittree); + + if (node_act == nullptr) { + return OPERATOR_CANCELLED; + } + + bNode *node; + bool changed = false; + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const int type = RNA_enum_get(op->ptr, "type"); + + if (!extend) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + nodeSetSelected(node, false); + } + } + nodeSetSelected(node_act, true); + + switch (type) { + case NODE_SELECT_GROUPED_TYPE: + changed = node_select_grouped_type(snode, node_act); + break; + case NODE_SELECT_GROUPED_COLOR: + changed = node_select_grouped_color(snode, node_act); + break; + case NODE_SELECT_GROUPED_PREFIX: + changed = node_select_grouped_name(snode, node_act, false); + break; + case NODE_SELECT_GROUPED_SUFIX: + changed = node_select_grouped_name(snode, node_act, true); + break; + default: + break; + } + + if (changed) { + ED_node_sort(snode->edittree); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void NODE_OT_select_grouped(wmOperatorType *ot) +{ + static const EnumPropertyItem prop_select_grouped_types[] = { + {NODE_SELECT_GROUPED_TYPE, "TYPE", 0, "Type", ""}, + {NODE_SELECT_GROUPED_COLOR, "COLOR", 0, "Color", ""}, + {NODE_SELECT_GROUPED_PREFIX, "PREFIX", 0, "Prefix", ""}, + {NODE_SELECT_GROUPED_SUFIX, "SUFFIX", 0, "Suffix", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + /* identifiers */ + ot->name = "Select Grouped"; + ot->description = "Select nodes with similar properties"; + ot->idname = "NODE_OT_select_grouped"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = node_select_grouped_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, + "extend", + false, + "Extend", + "Extend selection instead of deselecting everything first"); + ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", ""); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select (Cursor Pick) Operator + * \{ */ + +void node_select_single(bContext *C, bNode *node) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + const Object *ob = CTX_data_active_object(C); + const Scene *scene = CTX_data_scene(C); + const wmWindowManager *wm = CTX_wm_manager(C); + bool active_texture_changed = false; + bNode *tnode; + + for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) { + if (tnode != node) { + nodeSetSelected(tnode, false); + } + } + nodeSetSelected(node, true); + + ED_node_set_active(bmain, snode->edittree, node, &active_texture_changed); + ED_node_set_active_viewer_key(snode); + + ED_node_sort(snode->edittree); + if (active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) { + DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE); + } + + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); +} + +static int node_mouse_select(bContext *C, + wmOperator *op, + const int mval[2], + bool wait_to_deselect_others) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + const Object *ob = CTX_data_active_object(C); + const Scene *scene = CTX_data_scene(C); + const wmWindowManager *wm = CTX_wm_manager(C); + bNode *node, *tnode; + bNodeSocket *sock = nullptr; + bNodeSocket *tsock; + float cursor[2]; + int ret_value = OPERATOR_CANCELLED; + + const bool extend = RNA_boolean_get(op->ptr, "extend"); + /* always do socket_select when extending selection. */ + const bool socket_select = extend || RNA_boolean_get(op->ptr, "socket_select"); + const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + + /* These cases are never modal. */ + if (extend || socket_select) { + wait_to_deselect_others = false; + } + + /* get mouse coordinates in view2d space */ + UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &cursor[0], &cursor[1]); + + /* first do socket selection, these generally overlap with nodes. */ + if (socket_select) { + if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) { + /* NOTE: SOCK_IN does not take into account the extend case... + * This feature is not really used anyway currently? */ + node_socket_toggle(node, sock, true); + ret_value = OPERATOR_FINISHED; + } + else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) { + if (sock->flag & SELECT) { + if (extend) { + node_socket_deselect(node, sock, true); + } + else { + ret_value = OPERATOR_FINISHED; + } + } + else { + /* Only allow one selected output per node, for sensible linking. + * Allow selecting outputs from different nodes though, if extend is true. */ + if (node) { + for (tsock = (bNodeSocket *)node->outputs.first; tsock; tsock = tsock->next) { + if (tsock == sock) { + continue; + } + node_socket_deselect(node, tsock, true); + } + } + if (!extend) { + for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) { + if (tnode == node) { + continue; + } + for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) { + node_socket_deselect(tnode, tsock, true); + } + } + } + node_socket_select(node, sock); + ret_value = OPERATOR_FINISHED; + } + } + } + + if (!sock) { + /* find the closest visible node */ + node = node_under_mouse_select(snode->edittree, (int)cursor[0], (int)cursor[1]); + + if (extend) { + if (node != nullptr) { + /* If node is selected but not active, we want to make it active, + * but not toggle (deselect) it. */ + if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) { + node_toggle(node); + } + ret_value = OPERATOR_FINISHED; + } + } + else if (deselect_all && node == nullptr) { + /* Rather than deselecting others, users may want to drag to box-select (drag from empty + * space) or tweak-translate an already selected item. If these cases may apply, delay + * deselection. */ + if (wait_to_deselect_others) { + ret_value = OPERATOR_RUNNING_MODAL; + } + else { + /* Deselect in empty space. */ + for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) { + nodeSetSelected(tnode, false); + } + ret_value = OPERATOR_FINISHED; + } + } + else if (node != nullptr) { + /* When clicking on an already selected node, we want to wait to deselect + * others and allow the user to start moving the node without that. */ + if (wait_to_deselect_others && (node->flag & SELECT)) { + ret_value = OPERATOR_RUNNING_MODAL; + } + else { + nodeSetSelected(node, true); + + for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) { + if (tnode != node) { + nodeSetSelected(tnode, false); + } + } + + ret_value = OPERATOR_FINISHED; + } + } + } + + /* update node order */ + if (ret_value != OPERATOR_CANCELLED) { + bool active_texture_changed = false; + if (node != nullptr && ret_value != OPERATOR_RUNNING_MODAL) { + ED_node_set_active(bmain, snode->edittree, node, &active_texture_changed); + } + ED_node_set_active_viewer_key(snode); + ED_node_sort(snode->edittree); + if (active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) { + DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE); + } + + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); + } + + return ret_value; +} + +static int node_select_exec(bContext *C, wmOperator *op) +{ + const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); + + /* get settings from RNA properties for operator */ + int mval[2]; + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); + + /* perform the select */ + const int ret_value = node_mouse_select(C, op, mval, wait_to_deselect_others); + + /* allow tweak event to work too */ + return ret_value | OPERATOR_PASS_THROUGH; +} + +void NODE_OT_select(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Select"; + ot->idname = "NODE_OT_select"; + ot->description = "Select the node under the cursor"; + + /* api callbacks */ + ot->exec = node_select_exec; + ot->invoke = WM_generic_select_invoke; + ot->modal = WM_generic_select_modal; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_generic_select(ot); + RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); + RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); + 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); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Box Select Operator + * \{ */ + +static int node_box_select_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + rctf rectf; + + WM_operator_properties_border_to_rctf(op, &rectf); + UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); + + const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode"); + const bool select = (sel_op != SEL_OP_SUB); + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT); + } + + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + bool is_inside; + if (node->type == NODE_FRAME) { + is_inside = BLI_rctf_inside_rctf(&rectf, &node->totr); + } + else { + is_inside = BLI_rctf_isect(&rectf, &node->totr, nullptr); + } + + if (is_inside) { + nodeSetSelected(node, select); + } + } + + ED_node_sort(snode->edittree); + + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); + + return OPERATOR_FINISHED; +} + +static int node_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const bool tweak = RNA_boolean_get(op->ptr, "tweak"); + + if (tweak && is_event_over_node_or_socket(C, event)) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + return WM_gesture_box_invoke(C, op, event); +} + +void NODE_OT_select_box(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Box Select"; + ot->idname = "NODE_OT_select_box"; + ot->description = "Use box selection to select nodes"; + + /* api callbacks */ + ot->invoke = node_box_select_invoke; + ot->exec = node_box_select_exec; + ot->modal = WM_gesture_box_modal; + ot->cancel = WM_gesture_box_cancel; + + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, + "tweak", + false, + "Tweak", + "Only activate when mouse is not over a node (useful for tweak gesture)"); + + WM_operator_properties_gesture_box(ot); + WM_operator_properties_select_operation_simple(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Circle Select Operator + * \{ */ + +static int node_circleselect_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + bNode *node; + + int x, y, radius; + float offset[2]; + + float zoom = (float)(BLI_rcti_size_x(®ion->winrct)) / + (float)(BLI_rctf_size_x(®ion->v2d.cur)); + + const eSelectOp sel_op = ED_select_op_modal( + (eSelectOp)RNA_enum_get(op->ptr, "mode"), + WM_gesture_is_modal_first((const wmGesture *)op->customdata)); + const bool select = (sel_op != SEL_OP_SUB); + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT); + } + + /* get operator properties */ + x = RNA_int_get(op->ptr, "x"); + y = RNA_int_get(op->ptr, "y"); + radius = RNA_int_get(op->ptr, "radius"); + + UI_view2d_region_to_view(®ion->v2d, x, y, &offset[0], &offset[1]); + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + if (BLI_rctf_isect_circle(&node->totr, offset, radius / zoom)) { + nodeSetSelected(node, select); + } + } + + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_select_circle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Circle Select"; + ot->idname = "NODE_OT_select_circle"; + ot->description = "Use circle selection to select nodes"; + + /* api callbacks */ + ot->invoke = WM_gesture_circle_invoke; + ot->exec = node_circleselect_exec; + ot->modal = WM_gesture_circle_modal; + + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_gesture_circle(ot); + WM_operator_properties_select_operation_simple(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lasso Select Operator + * \{ */ + +static int node_lasso_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const bool tweak = RNA_boolean_get(op->ptr, "tweak"); + + if (tweak && is_event_over_node_or_socket(C, event)) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + return WM_gesture_lasso_invoke(C, op, event); +} + +static bool do_lasso_select_node(bContext *C, + const int mcoords[][2], + const int mcoords_len, + eSelectOp sel_op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node; + + ARegion *region = CTX_wm_region(C); + + rcti rect; + bool changed = false; + + const bool select = (sel_op != SEL_OP_SUB); + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT); + changed = true; + } + + /* get rectangle from operator */ + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); + + /* do actual selection */ + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + + if (select && (node->flag & NODE_SELECT)) { + continue; + } + + int screen_co[2]; + const float cent[2] = {BLI_rctf_cent_x(&node->totr), BLI_rctf_cent_y(&node->totr)}; + + /* marker in screen coords */ + if (UI_view2d_view_to_region_clip( + ®ion->v2d, cent[0], cent[1], &screen_co[0], &screen_co[1]) && + BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) && + BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co[0], screen_co[1], INT_MAX)) { + nodeSetSelected(node, select); + changed = true; + } + } + + if (changed) { + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); + } + + return changed; +} + +static int node_lasso_select_exec(bContext *C, wmOperator *op) +{ + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); + + if (mcoords) { + const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode"); + + do_lasso_select_node(C, mcoords, mcoords_len, sel_op); + + MEM_freeN((void *)mcoords); + + return OPERATOR_FINISHED; + } + return OPERATOR_PASS_THROUGH; +} + +void NODE_OT_select_lasso(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lasso Select"; + ot->description = "Select nodes using lasso selection"; + ot->idname = "NODE_OT_select_lasso"; + + /* api callbacks */ + ot->invoke = node_lasso_select_invoke; + ot->modal = WM_gesture_lasso_modal; + ot->exec = node_lasso_select_exec; + ot->poll = ED_operator_node_active; + ot->cancel = WM_gesture_lasso_cancel; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, + "tweak", + false, + "Tweak", + "Only activate when mouse is not over a node (useful for tweak gesture)"); + + WM_operator_properties_gesture_lasso(ot); + WM_operator_properties_select_operation_simple(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name (De)select All Operator + * \{ */ + +static int node_select_all_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ListBase *node_lb = &snode->edittree->nodes; + int action = RNA_enum_get(op->ptr, "action"); + + ED_node_select_all(node_lb, action); + + ED_node_sort(snode->edittree); + + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); + return OPERATOR_FINISHED; +} + +void NODE_OT_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "(De)select All"; + ot->description = "(De)select all nodes"; + ot->idname = "NODE_OT_select_all"; + + /* api callbacks */ + ot->exec = node_select_all_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + WM_operator_properties_select_all(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked To Operator + * \{ */ + +static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeLink *link; + bNode *node; + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + node->flag &= ~NODE_TEST; + } + + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { + if (nodeLinkIsHidden(link)) { + continue; + } + if (link->fromnode && link->tonode && (link->fromnode->flag & NODE_SELECT)) { + link->tonode->flag |= NODE_TEST; + } + } + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + if (node->flag & NODE_TEST) { + nodeSetSelected(node, true); + } + } + + ED_node_sort(snode->edittree); + + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); + return OPERATOR_FINISHED; +} + +void NODE_OT_select_linked_to(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked To"; + ot->description = "Select nodes linked to the selected ones"; + ot->idname = "NODE_OT_select_linked_to"; + + /* api callbacks */ + ot->exec = node_select_linked_to_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked From Operator + * \{ */ + +static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeLink *link; + bNode *node; + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + node->flag &= ~NODE_TEST; + } + + for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { + if (nodeLinkIsHidden(link)) { + continue; + } + if (link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT)) { + link->fromnode->flag |= NODE_TEST; + } + } + + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + if (node->flag & NODE_TEST) { + nodeSetSelected(node, true); + } + } + + ED_node_sort(snode->edittree); + + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); + return OPERATOR_FINISHED; +} + +void NODE_OT_select_linked_from(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked From"; + ot->description = "Select nodes linked from the selected ones"; + ot->idname = "NODE_OT_select_linked_from"; + + /* api callbacks */ + ot->exec = node_select_linked_from_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Same Type Step Operator + * \{ */ + +static int node_select_same_type_step_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + bNode **node_array; + bNode *active = nodeGetActive(snode->edittree); + int totnodes; + const bool revert = RNA_boolean_get(op->ptr, "prev"); + const bool same_type = true; + + ntreeGetDependencyList(snode->edittree, &node_array, &totnodes); + + if (totnodes > 1) { + int a; + + for (a = 0; a < totnodes; a++) { + if (node_array[a] == active) { + break; + } + } + + if (same_type) { + bNode *node = nullptr; + + while (node == nullptr) { + if (revert) { + a--; + } + else { + a++; + } + + if (a < 0 || a >= totnodes) { + break; + } + + node = node_array[a]; + + if (node->type == active->type) { + break; + } + node = nullptr; + } + if (node) { + active = node; + } + } + else { + if (revert) { + if (a == 0) { + active = node_array[totnodes - 1]; + } + else { + active = node_array[a - 1]; + } + } + else { + if (a == totnodes - 1) { + active = node_array[0]; + } + else { + active = node_array[a + 1]; + } + } + } + + node_select_single(C, active); + + /* is note outside view? */ + if (active->totr.xmax < region->v2d.cur.xmin || active->totr.xmin > region->v2d.cur.xmax || + active->totr.ymax < region->v2d.cur.ymin || active->totr.ymin > region->v2d.cur.ymax) { + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + space_node_view_flag(C, snode, region, NODE_SELECT, smooth_viewtx); + } + } + + if (node_array) { + MEM_freeN(node_array); + } + + return OPERATOR_FINISHED; +} + +void NODE_OT_select_same_type_step(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Activate Same Type Next/Prev"; + ot->description = "Activate and view same node type, step by step"; + ot->idname = "NODE_OT_select_same_type_step"; + + /* api callbacks */ + ot->exec = node_select_same_type_step_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "prev", false, "Previous", ""); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Find Node by Name Operator + * \{ */ + +static void node_find_create_label(const bNode *node, char *str, int maxlen) +{ + if (node->label[0]) { + BLI_snprintf(str, maxlen, "%s (%s)", node->name, node->label); + } + else { + BLI_strncpy(str, node->name, maxlen); + } +} + +/* generic search invoke */ +static void node_find_update_fn(const struct bContext *C, + void *UNUSED(arg), + const char *str, + uiSearchItems *items, + const bool UNUSED(is_first)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + StringSearch *search = BLI_string_search_new(); + + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { + char name[256]; + node_find_create_label(node, name, ARRAY_SIZE(name)); + BLI_string_search_add(search, name, node); + } + + bNode **filtered_nodes; + int filtered_amount = BLI_string_search_query(search, str, (void ***)&filtered_nodes); + + for (int i = 0; i < filtered_amount; i++) { + bNode *node = filtered_nodes[i]; + char name[256]; + node_find_create_label(node, name, ARRAY_SIZE(name)); + if (!UI_search_item_add(items, name, node, ICON_NONE, 0, 0)) { + break; + } + } + + MEM_freeN(filtered_nodes); + BLI_string_search_free(search); +} + +static void node_find_exec_fn(struct bContext *C, void *UNUSED(arg1), void *arg2) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNode *active = (bNode *)arg2; + + if (active) { + ARegion *region = CTX_wm_region(C); + node_select_single(C, active); + + /* is note outside view? */ + if (active->totr.xmax < region->v2d.cur.xmin || active->totr.xmin > region->v2d.cur.xmax || + active->totr.ymax < region->v2d.cur.ymin || active->totr.ymin > region->v2d.cur.ymax) { + space_node_view_flag(C, snode, region, NODE_SELECT, U.smooth_viewtx); + } + } +} + +static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op) +{ + static char search[256] = ""; + uiBlock *block; + uiBut *but; + wmOperator *op = (wmOperator *)arg_op; + + block = UI_block_begin(C, region, "_popup", UI_EMBOSS); + UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + + but = uiDefSearchBut(block, + search, + 0, + ICON_VIEWZOOM, + sizeof(search), + 10, + 10, + UI_searchbox_size_x(), + UI_UNIT_Y, + 0, + 0, + ""); + UI_but_func_search_set( + but, nullptr, node_find_update_fn, op->type, false, nullptr, node_find_exec_fn, nullptr); + UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); + + /* fake button, it holds space for search items */ + uiDefBut(block, + UI_BTYPE_LABEL, + 0, + "", + 10, + 10 - UI_searchbox_size_y(), + UI_searchbox_size_x(), + UI_searchbox_size_y(), + nullptr, + 0, + 0, + 0, + 0, + nullptr); + + /* Move it downwards, mouse over button. */ + std::array bounds_offset = {0, -UI_UNIT_Y}; + UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, bounds_offset.data()); + + return block; +} + +static int node_find_node_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + UI_popup_block_invoke(C, node_find_menu, op, nullptr); + return OPERATOR_CANCELLED; +} + +void NODE_OT_find_node(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Find Node"; + ot->description = "Search for a node by name and focus and select it"; + ot->idname = "NODE_OT_find_node"; + + /* api callbacks */ + ot->invoke = node_find_node_invoke; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "prev", false, "Previous", ""); +} + +/** \} */ diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c deleted file mode 100644 index c880f3e99d8..00000000000 --- a/source/blender/editors/space_node/node_templates.c +++ /dev/null @@ -1,892 +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. - */ - -/** \file - * \ingroup edinterface - */ - -#include -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_node_types.h" -#include "DNA_screen_types.h" - -#include "BLI_array.h" -#include "BLI_listbase.h" -#include "BLI_string.h" - -#include "BLT_translation.h" - -#include "BKE_context.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" - -#include "RNA_access.h" - -#include "NOD_socket.h" - -#include "../interface/interface_intern.h" /* XXX bad level */ -#include "UI_interface.h" - -#include "ED_node.h" /* own include */ -#include "node_intern.h" - -#include "ED_undo.h" - -/************************* Node Socket Manipulation **************************/ - -/* describes an instance of a node type and a specific socket to link */ -typedef struct NodeLinkItem { - int socket_index; /* index for linking */ - int socket_type; /* socket type for compatibility check */ - const char *socket_name; /* ui label of the socket */ - const char *node_name; /* ui label of the node */ - - /* extra settings */ - bNodeTree *ngroup; /* group node tree */ -} NodeLinkItem; - -/* Compare an existing node to a link item to see if it can be reused. - * item must be for the same node type! - * XXX should become a node type callback - */ -static bool node_link_item_compare(bNode *node, NodeLinkItem *item) -{ - if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { - return (node->id == (ID *)item->ngroup); - } - return true; -} - -static void node_link_item_apply(Main *bmain, bNode *node, NodeLinkItem *item) -{ - if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { - node->id = (ID *)item->ngroup; - ntreeUpdateTree(bmain, item->ngroup); - } - else { - /* nothing to do for now */ - } - - if (node->id) { - id_us_plus(node->id); - } -} - -static void node_tag_recursive(bNode *node) -{ - bNodeSocket *input; - - if (!node || (node->flag & NODE_TEST)) { - return; /* in case of cycles */ - } - - node->flag |= NODE_TEST; - - for (input = node->inputs.first; input; input = input->next) { - if (input->link) { - node_tag_recursive(input->link->fromnode); - } - } -} - -static void node_clear_recursive(bNode *node) -{ - bNodeSocket *input; - - if (!node || !(node->flag & NODE_TEST)) { - return; /* in case of cycles */ - } - - node->flag &= ~NODE_TEST; - - for (input = node->inputs.first; input; input = input->next) { - if (input->link) { - node_clear_recursive(input->link->fromnode); - } - } -} - -static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node) -{ - bNode *node, *next; - bNodeSocket *sock; - - if (!rem_node) { - return; - } - - /* tag linked nodes to be removed */ - for (node = ntree->nodes.first; node; node = node->next) { - node->flag &= ~NODE_TEST; - } - - node_tag_recursive(rem_node); - - /* clear tags on nodes that are still used by other nodes */ - for (node = ntree->nodes.first; node; node = node->next) { - if (!(node->flag & NODE_TEST)) { - for (sock = node->inputs.first; sock; sock = sock->next) { - if (sock->link && sock->link->fromnode != rem_node) { - node_clear_recursive(sock->link->fromnode); - } - } - } - } - - /* remove nodes */ - for (node = ntree->nodes.first; node; node = next) { - next = node->next; - - if (node->flag & NODE_TEST) { - nodeRemoveNode(bmain, ntree, node, true); - } - } -} - -/* disconnect socket from the node it is connected to */ -static void node_socket_disconnect(Main *bmain, - bNodeTree *ntree, - bNode *node_to, - bNodeSocket *sock_to) -{ - if (!sock_to->link) { - return; - } - - nodeRemLink(ntree, sock_to->link); - sock_to->flag |= SOCK_COLLAPSED; - - nodeUpdate(ntree, node_to); - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, node_to); -} - -/* remove all nodes connected to this socket, if they aren't connected to other nodes */ -static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to) -{ - if (!sock_to->link) { - return; - } - - node_remove_linked(bmain, ntree, sock_to->link->fromnode); - sock_to->flag |= SOCK_COLLAPSED; - - nodeUpdate(ntree, node_to); - ntreeUpdateTree(bmain, ntree); - - ED_node_tag_update_nodetree(bmain, ntree, node_to); -} - -/* add new node connected to this socket, or replace an existing one */ -static void node_socket_add_replace(const bContext *C, - bNodeTree *ntree, - bNode *node_to, - bNodeSocket *sock_to, - int type, - NodeLinkItem *item) -{ - Main *bmain = CTX_data_main(C); - bNode *node_from; - bNodeSocket *sock_from_tmp; - bNode *node_prev = NULL; - - /* unlink existing node */ - if (sock_to->link) { - node_prev = sock_to->link->fromnode; - nodeRemLink(ntree, sock_to->link); - } - - /* find existing node that we can use */ - for (node_from = ntree->nodes.first; node_from; node_from = node_from->next) { - if (node_from->type == type) { - break; - } - } - - if (node_from) { - if (node_from->inputs.first || node_from->typeinfo->draw_buttons || - node_from->typeinfo->draw_buttons_ex) { - node_from = NULL; - } - } - - if (node_prev && node_prev->type == type && node_link_item_compare(node_prev, item)) { - /* keep the previous node if it's the same type */ - node_from = node_prev; - } - else if (!node_from) { - node_from = nodeAddStaticNode(C, ntree, type); - if (node_prev != NULL) { - /* If we're replacing existing node, use its location. */ - node_from->locx = node_prev->locx; - node_from->locy = node_prev->locy; - node_from->offsetx = node_prev->offsetx; - node_from->offsety = node_prev->offsety; - } - else { - sock_from_tmp = BLI_findlink(&node_from->outputs, item->socket_index); - nodePositionRelative(node_from, node_to, sock_from_tmp, sock_to); - } - - node_link_item_apply(bmain, node_from, item); - } - - nodeSetActive(ntree, node_from); - - /* add link */ - sock_from_tmp = BLI_findlink(&node_from->outputs, item->socket_index); - nodeAddLink(ntree, node_from, sock_from_tmp, node_to, sock_to); - sock_to->flag &= ~SOCK_COLLAPSED; - - /* copy input sockets from previous node */ - if (node_prev && node_from != node_prev) { - bNodeSocket *sock_prev, *sock_from; - - for (sock_prev = node_prev->inputs.first; sock_prev; sock_prev = sock_prev->next) { - for (sock_from = node_from->inputs.first; sock_from; sock_from = sock_from->next) { - if (nodeCountSocketLinks(ntree, sock_from) >= nodeSocketLinkLimit(sock_from)) { - continue; - } - - if (STREQ(sock_prev->name, sock_from->name) && sock_prev->type == sock_from->type) { - bNodeLink *link = sock_prev->link; - - if (link && link->fromnode) { - nodeAddLink(ntree, link->fromnode, link->fromsock, node_from, sock_from); - nodeRemLink(ntree, link); - } - - node_socket_copy_default_value(sock_from, sock_prev); - } - } - } - - /* also preserve mapping for texture nodes */ - if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE && - node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE && - /* White noise texture node does not have NodeTexBase. */ - node_from->storage != NULL && node_prev->storage != NULL) { - memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase)); - } - - /* remove node */ - node_remove_linked(bmain, ntree, node_prev); - } - - nodeUpdate(ntree, node_from); - nodeUpdate(ntree, node_to); - ntreeUpdateTree(CTX_data_main(C), ntree); - - ED_node_tag_update_nodetree(CTX_data_main(C), ntree, node_to); -} - -/****************************** Node Link Menu *******************************/ - -// #define UI_NODE_LINK_ADD 0 -#define UI_NODE_LINK_DISCONNECT -1 -#define UI_NODE_LINK_REMOVE -2 - -typedef struct NodeLinkArg { - Main *bmain; - Scene *scene; - bNodeTree *ntree; - bNode *node; - bNodeSocket *sock; - - bNodeType *node_type; - NodeLinkItem item; - - uiLayout *layout; -} NodeLinkArg; - -static void ui_node_link_items(NodeLinkArg *arg, - int in_out, - NodeLinkItem **r_items, - int *r_totitems) -{ - /* XXX this should become a callback for node types! */ - NodeLinkItem *items = NULL; - int totitems = 0; - - if (arg->node_type->type == NODE_GROUP) { - bNodeTree *ngroup; - int i; - - for (ngroup = arg->bmain->nodetrees.first; ngroup; ngroup = ngroup->id.next) { - const char *disabled_hint; - if ((ngroup->type != arg->ntree->type) || - !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { - continue; - } - - ListBase *lb = ((in_out == SOCK_IN) ? &ngroup->inputs : &ngroup->outputs); - totitems += BLI_listbase_count(lb); - } - - if (totitems > 0) { - items = MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); - - i = 0; - for (ngroup = arg->bmain->nodetrees.first; ngroup; ngroup = ngroup->id.next) { - const char *disabled_hint; - if ((ngroup->type != arg->ntree->type) || - !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { - continue; - } - - ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs); - bNodeSocket *stemp; - int index; - for (stemp = lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) { - NodeLinkItem *item = &items[i]; - - item->socket_index = index; - /* note: int stemp->type is not fully reliable, not used for node group - * interface sockets. use the typeinfo->type instead. - */ - item->socket_type = stemp->typeinfo->type; - item->socket_name = stemp->name; - item->node_name = ngroup->id.name + 2; - item->ngroup = ngroup; - } - } - } - } - else { - bNodeSocketTemplate *socket_templates = (in_out == SOCK_IN ? arg->node_type->inputs : - arg->node_type->outputs); - bNodeSocketTemplate *stemp; - int i; - - for (stemp = socket_templates; stemp && stemp->type != -1; stemp++) { - totitems++; - } - - if (totitems > 0) { - items = MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); - - i = 0; - for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) { - NodeLinkItem *item = &items[i]; - - item->socket_index = i; - item->socket_type = stemp->type; - item->socket_name = stemp->name; - item->node_name = arg->node_type->ui_name; - } - } - } - - *r_items = items; - *r_totitems = totitems; -} - -static void ui_node_link(bContext *C, void *arg_p, void *event_p) -{ - NodeLinkArg *arg = (NodeLinkArg *)arg_p; - Main *bmain = arg->bmain; - bNode *node_to = arg->node; - bNodeSocket *sock_to = arg->sock; - bNodeTree *ntree = arg->ntree; - int event = POINTER_AS_INT(event_p); - - if (event == UI_NODE_LINK_DISCONNECT) { - node_socket_disconnect(bmain, ntree, node_to, sock_to); - } - else if (event == UI_NODE_LINK_REMOVE) { - node_socket_remove(bmain, ntree, node_to, sock_to); - } - else { - node_socket_add_replace(C, ntree, node_to, sock_to, arg->node_type->type, &arg->item); - } - - ED_undo_push(C, "Node input modify"); -} - -static void ui_node_sock_name(bNodeTree *ntree, bNodeSocket *sock, char name[UI_MAX_NAME_STR]) -{ - if (sock->link && sock->link->fromnode) { - bNode *node = sock->link->fromnode; - char node_name[UI_MAX_NAME_STR]; - - nodeLabel(ntree, node, node_name, sizeof(node_name)); - - if (BLI_listbase_is_empty(&node->inputs) && node->outputs.first != node->outputs.last) { - BLI_snprintf( - name, UI_MAX_NAME_STR, "%s | %s", IFACE_(node_name), IFACE_(sock->link->fromsock->name)); - } - else { - BLI_strncpy(name, IFACE_(node_name), UI_MAX_NAME_STR); - } - } - else if (sock->type == SOCK_SHADER) { - BLI_strncpy(name, IFACE_("None"), UI_MAX_NAME_STR); - } - else { - BLI_strncpy(name, IFACE_("Default"), UI_MAX_NAME_STR); - } -} - -static int ui_compatible_sockets(int typeA, int typeB) -{ - return (typeA == typeB); -} - -static int ui_node_item_name_compare(const void *a, const void *b) -{ - const bNodeType *type_a = *(const bNodeType **)a; - const bNodeType *type_b = *(const bNodeType **)b; - return BLI_strcasecmp_natural(type_a->ui_name, type_b->ui_name); -} - -static bool ui_node_item_special_poll(const bNodeTree *UNUSED(ntree), const bNodeType *ntype) -{ - if (STREQ(ntype->idname, "ShaderNodeUVAlongStroke")) { - /* TODO(sergey): Currently we don't have Freestyle nodes edited from - * the buttons context, so can ignore its nodes completely. - * - * However, we might want to do some extra checks here later. - */ - return false; - } - return true; -} - -static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) -{ - bNodeTree *ntree = arg->ntree; - bNodeSocket *sock = arg->sock; - uiLayout *layout = arg->layout; - uiLayout *column = NULL; - uiBlock *block = uiLayoutGetBlock(layout); - uiBut *but; - NodeLinkArg *argN; - int first = 1; - - /* generate array of node types sorted by UI name */ - bNodeType **sorted_ntypes = NULL; - BLI_array_declare(sorted_ntypes); - - NODE_TYPES_BEGIN (ntype) { - const char *disabled_hint; - if (!(ntype->poll && ntype->poll(ntype, ntree, &disabled_hint))) { - continue; - } - - if (ntype->nclass != nclass) { - continue; - } - - if (!ui_node_item_special_poll(ntree, ntype)) { - continue; - } - - BLI_array_append(sorted_ntypes, ntype); - } - NODE_TYPES_END; - - qsort( - sorted_ntypes, BLI_array_len(sorted_ntypes), sizeof(bNodeType *), ui_node_item_name_compare); - - /* generate UI */ - for (int j = 0; j < BLI_array_len(sorted_ntypes); j++) { - bNodeType *ntype = sorted_ntypes[j]; - NodeLinkItem *items; - int totitems; - char name[UI_MAX_NAME_STR]; - const char *cur_node_name = NULL; - int num = 0; - int icon = ICON_NONE; - - arg->node_type = ntype; - - ui_node_link_items(arg, SOCK_OUT, &items, &totitems); - - for (int i = 0; i < totitems; i++) { - if (ui_compatible_sockets(items[i].socket_type, sock->type)) { - num++; - } - } - - for (int i = 0; i < totitems; i++) { - if (!ui_compatible_sockets(items[i].socket_type, sock->type)) { - continue; - } - - if (first) { - column = uiLayoutColumn(layout, 0); - UI_block_layout_set_current(block, column); - - uiItemL(column, IFACE_(cname), ICON_NODE); - but = block->buttons.last; - - first = 0; - } - - if (num > 1) { - if (!cur_node_name || !STREQ(cur_node_name, items[i].node_name)) { - cur_node_name = items[i].node_name; - /* XXX Do not use uiItemL here, - * it would add an empty icon as we are in a menu! */ - uiDefBut(block, - UI_BTYPE_LABEL, - 0, - IFACE_(cur_node_name), - 0, - 0, - UI_UNIT_X * 4, - UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - ""); - } - - BLI_snprintf(name, UI_MAX_NAME_STR, "%s", IFACE_(items[i].socket_name)); - icon = ICON_BLANK1; - } - else { - BLI_strncpy(name, IFACE_(items[i].node_name), UI_MAX_NAME_STR); - icon = ICON_NONE; - } - - but = uiDefIconTextBut(block, - UI_BTYPE_BUT, - 0, - icon, - name, - 0, - 0, - UI_UNIT_X * 4, - UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Add node to input")); - - argN = MEM_dupallocN(arg); - argN->item = items[i]; - UI_but_funcN_set(but, ui_node_link, argN, NULL); - } - - if (items) { - MEM_freeN(items); - } - } - - BLI_array_free(sorted_ntypes); -} - -static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name) -{ - NodeLinkArg *arg = (NodeLinkArg *)calldata; - - if (!ELEM(nclass, NODE_CLASS_GROUP, NODE_CLASS_LAYOUT)) { - ui_node_menu_column(arg, nclass, name); - } -} - -static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - uiBlock *block = uiLayoutGetBlock(layout); - uiBut *but = (uiBut *)but_p; - uiLayout *split, *column; - NodeLinkArg *arg = (NodeLinkArg *)but->func_argN; - bNodeSocket *sock = arg->sock; - bNodeTreeType *ntreetype = arg->ntree->typeinfo; - - UI_block_flag_enable(block, UI_BLOCK_NO_FLIP | UI_BLOCK_IS_FLIP); - UI_block_layout_set_current(block, layout); - split = uiLayoutSplit(layout, 0.0f, false); - - arg->bmain = bmain; - arg->scene = scene; - arg->layout = split; - - if (ntreetype && ntreetype->foreach_nodeclass) { - ntreetype->foreach_nodeclass(scene, arg, node_menu_column_foreach_cb); - } - - column = uiLayoutColumn(split, false); - UI_block_layout_set_current(block, column); - - if (sock->link) { - uiItemL(column, IFACE_("Link"), ICON_NONE); - but = block->buttons.last; - but->drawflag = UI_BUT_TEXT_LEFT; - - but = uiDefBut(block, - UI_BTYPE_BUT, - 0, - IFACE_("Remove"), - 0, - 0, - UI_UNIT_X * 4, - UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Remove nodes connected to the input")); - UI_but_funcN_set(but, ui_node_link, MEM_dupallocN(arg), POINTER_FROM_INT(UI_NODE_LINK_REMOVE)); - - but = uiDefBut(block, - UI_BTYPE_BUT, - 0, - IFACE_("Disconnect"), - 0, - 0, - UI_UNIT_X * 4, - UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Disconnect nodes connected to the input")); - UI_but_funcN_set( - but, ui_node_link, MEM_dupallocN(arg), POINTER_FROM_INT(UI_NODE_LINK_DISCONNECT)); - } - - ui_node_menu_column(arg, NODE_CLASS_GROUP, N_("Group")); -} - -void uiTemplateNodeLink( - uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input) -{ - uiBlock *block = uiLayoutGetBlock(layout); - NodeLinkArg *arg; - uiBut *but; - float socket_col[4]; - - arg = MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg"); - arg->ntree = ntree; - arg->node = node; - arg->sock = input; - - PointerRNA node_ptr; - RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); - node_socket_color_get(C, ntree, &node_ptr, input, socket_col); - - UI_block_layout_set_current(block, layout); - - if (input->link || input->type == SOCK_SHADER || (input->flag & SOCK_HIDE_VALUE)) { - char name[UI_MAX_NAME_STR]; - ui_node_sock_name(ntree, input, name); - but = uiDefMenuBut( - block, ui_template_node_link_menu, NULL, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, ""); - } - else { - but = uiDefIconMenuBut( - block, ui_template_node_link_menu, NULL, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, ""); - } - - UI_but_type_set_menu_from_pulldown(but); - UI_but_node_link_set(but, input, socket_col); - UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); - - but->poin = (char *)but; - but->func_argN = arg; - - if (input->link && input->link->fromnode) { - if (input->link->fromnode->flag & NODE_ACTIVE_TEXTURE) { - but->flag |= UI_BUT_NODE_ACTIVE; - } - } -} - -/**************************** Node Tree Layout *******************************/ - -static void ui_node_draw_input( - uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth); - -static void ui_node_draw_node( - uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, int depth) -{ - bNodeSocket *input; - PointerRNA nodeptr; - - RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); - - if (node->typeinfo->draw_buttons) { - if (node->type != NODE_GROUP) { - uiLayoutSetPropSep(layout, true); - node->typeinfo->draw_buttons(layout, C, &nodeptr); - } - } - - for (input = node->inputs.first; input; input = input->next) { - ui_node_draw_input(layout, C, ntree, node, input, depth + 1); - } -} - -static void ui_node_draw_input( - uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth) -{ - PointerRNA inputptr, nodeptr; - uiBlock *block = uiLayoutGetBlock(layout); - uiLayout *row = NULL; - bNode *lnode; - bool dependency_loop; - - if (input->flag & SOCK_UNAVAIL) { - return; - } - - /* to avoid eternal loops on cyclic dependencies */ - node->flag |= NODE_TEST; - lnode = (input->link) ? input->link->fromnode : NULL; - - dependency_loop = (lnode && (lnode->flag & NODE_TEST)); - if (dependency_loop) { - lnode = NULL; - } - - /* socket RNA pointer */ - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, input, &inputptr); - RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); - - row = uiLayoutRow(layout, true); - /* Decorations are added manually here. */ - uiLayoutSetPropDecorate(row, false); - - uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(row); - /* Empty decorator item for alignment. */ - bool add_dummy_decorator = false; - - { - uiLayout *sub = uiLayoutRow(split_wrapper.label_column, true); - - if (depth > 0) { - UI_block_emboss_set(block, UI_EMBOSS_NONE); - - if (lnode && - (lnode->inputs.first || (lnode->typeinfo->draw_buttons && lnode->type != NODE_GROUP))) { - int icon = (input->flag & SOCK_COLLAPSED) ? ICON_DISCLOSURE_TRI_RIGHT : - ICON_DISCLOSURE_TRI_DOWN; - uiItemR(sub, &inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon); - } - - UI_block_emboss_set(block, UI_EMBOSS); - } - - sub = uiLayoutRow(sub, true); - uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT); - uiItemL(sub, IFACE_(input->name), ICON_NONE); - } - - if (dependency_loop) { - uiItemL(row, IFACE_("Dependency Loop"), ICON_ERROR); - add_dummy_decorator = true; - } - else if (lnode) { - /* input linked to a node */ - uiTemplateNodeLink(row, C, ntree, node, input); - add_dummy_decorator = true; - - if (depth == 0 || !(input->flag & SOCK_COLLAPSED)) { - if (depth == 0) { - uiItemS(layout); - } - - ui_node_draw_node(layout, C, ntree, lnode, depth); - } - } - else { - row = uiLayoutRow(row, true); - - uiTemplateNodeLink(row, C, ntree, node, input); - - if (input->flag & SOCK_HIDE_VALUE) { - add_dummy_decorator = true; - } - /* input not linked, show value */ - else { - uiLayout *sub = row; - - switch (input->type) { - case SOCK_VECTOR: - if (input->type == SOCK_VECTOR) { - uiItemS(row); - sub = uiLayoutColumn(row, true); - } - ATTR_FALLTHROUGH; - case SOCK_FLOAT: - case SOCK_INT: - case SOCK_BOOLEAN: - case SOCK_RGBA: - uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE); - uiItemDecoratorR( - split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX); - break; - case SOCK_STRING: { - const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id; - if (node_tree->type == NTREE_GEOMETRY) { - node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, row); - } - else { - uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE); - } - uiItemDecoratorR( - split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX); - break; - } - default: - add_dummy_decorator = true; - } - } - } - - if (add_dummy_decorator) { - uiItemDecoratorR(split_wrapper.decorate_column, NULL, NULL, 0); - } - - /* clear */ - node->flag &= ~NODE_TEST; -} - -void uiTemplateNodeView( - uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input) -{ - bNode *tnode; - - if (!ntree) { - return; - } - - /* clear for cycle check */ - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { - tnode->flag &= ~NODE_TEST; - } - - if (input) { - ui_node_draw_input(layout, C, ntree, node, input, 0); - } - else { - ui_node_draw_node(layout, C, ntree, node, 0); - } -} diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc new file mode 100644 index 00000000000..fae180ca6c5 --- /dev/null +++ b/source/blender/editors/space_node/node_templates.cc @@ -0,0 +1,895 @@ +/* + * 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. + */ + +/** \file + * \ingroup edinterface + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_node_types.h" +#include "DNA_screen_types.h" + +#include "BLI_array.h" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_vector.hh" + +#include "BLT_translation.h" + +#include "BKE_context.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" + +#include "RNA_access.h" + +#include "NOD_socket.h" + +#include "../interface/interface_intern.h" /* XXX bad level */ +#include "UI_interface.h" + +#include "ED_node.h" /* own include */ +#include "node_intern.h" + +#include "ED_undo.h" + +/************************* Node Socket Manipulation **************************/ + +/* describes an instance of a node type and a specific socket to link */ +struct NodeLinkItem { + int socket_index; /* index for linking */ + int socket_type; /* socket type for compatibility check */ + const char *socket_name; /* ui label of the socket */ + const char *node_name; /* ui label of the node */ + + /* extra settings */ + bNodeTree *ngroup; /* group node tree */ +}; + +/* Compare an existing node to a link item to see if it can be reused. + * item must be for the same node type! + * XXX should become a node type callback + */ +static bool node_link_item_compare(bNode *node, NodeLinkItem *item) +{ + if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { + return (node->id == (ID *)item->ngroup); + } + return true; +} + +static void node_link_item_apply(Main *bmain, bNode *node, NodeLinkItem *item) +{ + if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { + node->id = (ID *)item->ngroup; + ntreeUpdateTree(bmain, item->ngroup); + } + else { + /* nothing to do for now */ + } + + if (node->id) { + id_us_plus(node->id); + } +} + +static void node_tag_recursive(bNode *node) +{ + bNodeSocket *input; + + if (!node || (node->flag & NODE_TEST)) { + return; /* in case of cycles */ + } + + node->flag |= NODE_TEST; + + for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) { + if (input->link) { + node_tag_recursive(input->link->fromnode); + } + } +} + +static void node_clear_recursive(bNode *node) +{ + bNodeSocket *input; + + if (!node || !(node->flag & NODE_TEST)) { + return; /* in case of cycles */ + } + + node->flag &= ~NODE_TEST; + + for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) { + if (input->link) { + node_clear_recursive(input->link->fromnode); + } + } +} + +static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node) +{ + bNode *node, *next; + bNodeSocket *sock; + + if (!rem_node) { + return; + } + + /* tag linked nodes to be removed */ + for (node = (bNode *)ntree->nodes.first; node; node = node->next) { + node->flag &= ~NODE_TEST; + } + + node_tag_recursive(rem_node); + + /* clear tags on nodes that are still used by other nodes */ + for (node = (bNode *)ntree->nodes.first; node; node = node->next) { + if (!(node->flag & NODE_TEST)) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { + if (sock->link && sock->link->fromnode != rem_node) { + node_clear_recursive(sock->link->fromnode); + } + } + } + } + + /* remove nodes */ + for (node = (bNode *)ntree->nodes.first; node; node = next) { + next = node->next; + + if (node->flag & NODE_TEST) { + nodeRemoveNode(bmain, ntree, node, true); + } + } +} + +/* disconnect socket from the node it is connected to */ +static void node_socket_disconnect(Main *bmain, + bNodeTree *ntree, + bNode *node_to, + bNodeSocket *sock_to) +{ + if (!sock_to->link) { + return; + } + + nodeRemLink(ntree, sock_to->link); + sock_to->flag |= SOCK_COLLAPSED; + + nodeUpdate(ntree, node_to); + ntreeUpdateTree(bmain, ntree); + + ED_node_tag_update_nodetree(bmain, ntree, node_to); +} + +/* remove all nodes connected to this socket, if they aren't connected to other nodes */ +static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to) +{ + if (!sock_to->link) { + return; + } + + node_remove_linked(bmain, ntree, sock_to->link->fromnode); + sock_to->flag |= SOCK_COLLAPSED; + + nodeUpdate(ntree, node_to); + ntreeUpdateTree(bmain, ntree); + + ED_node_tag_update_nodetree(bmain, ntree, node_to); +} + +/* add new node connected to this socket, or replace an existing one */ +static void node_socket_add_replace(const bContext *C, + bNodeTree *ntree, + bNode *node_to, + bNodeSocket *sock_to, + int type, + NodeLinkItem *item) +{ + Main *bmain = CTX_data_main(C); + bNode *node_from; + bNodeSocket *sock_from_tmp; + bNode *node_prev = nullptr; + + /* unlink existing node */ + if (sock_to->link) { + node_prev = sock_to->link->fromnode; + nodeRemLink(ntree, sock_to->link); + } + + /* find existing node that we can use */ + for (node_from = (bNode *)ntree->nodes.first; node_from; node_from = node_from->next) { + if (node_from->type == type) { + break; + } + } + + if (node_from) { + if (node_from->inputs.first || node_from->typeinfo->draw_buttons || + node_from->typeinfo->draw_buttons_ex) { + node_from = nullptr; + } + } + + if (node_prev && node_prev->type == type && node_link_item_compare(node_prev, item)) { + /* keep the previous node if it's the same type */ + node_from = node_prev; + } + else if (!node_from) { + node_from = nodeAddStaticNode(C, ntree, type); + if (node_prev != nullptr) { + /* If we're replacing existing node, use its location. */ + node_from->locx = node_prev->locx; + node_from->locy = node_prev->locy; + node_from->offsetx = node_prev->offsetx; + node_from->offsety = node_prev->offsety; + } + else { + sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index); + nodePositionRelative(node_from, node_to, sock_from_tmp, sock_to); + } + + node_link_item_apply(bmain, node_from, item); + } + + nodeSetActive(ntree, node_from); + + /* add link */ + sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index); + nodeAddLink(ntree, node_from, sock_from_tmp, node_to, sock_to); + sock_to->flag &= ~SOCK_COLLAPSED; + + /* copy input sockets from previous node */ + if (node_prev && node_from != node_prev) { + bNodeSocket *sock_prev, *sock_from; + + for (sock_prev = (bNodeSocket *)node_prev->inputs.first; sock_prev; + sock_prev = sock_prev->next) { + for (sock_from = (bNodeSocket *)node_from->inputs.first; sock_from; + sock_from = sock_from->next) { + if (nodeCountSocketLinks(ntree, sock_from) >= nodeSocketLinkLimit(sock_from)) { + continue; + } + + if (STREQ(sock_prev->name, sock_from->name) && sock_prev->type == sock_from->type) { + bNodeLink *link = sock_prev->link; + + if (link && link->fromnode) { + nodeAddLink(ntree, link->fromnode, link->fromsock, node_from, sock_from); + nodeRemLink(ntree, link); + } + + node_socket_copy_default_value(sock_from, sock_prev); + } + } + } + + /* also preserve mapping for texture nodes */ + if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE && + node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE && + /* White noise texture node does not have NodeTexBase. */ + node_from->storage != nullptr && node_prev->storage != nullptr) { + memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase)); + } + + /* remove node */ + node_remove_linked(bmain, ntree, node_prev); + } + + nodeUpdate(ntree, node_from); + nodeUpdate(ntree, node_to); + ntreeUpdateTree(CTX_data_main(C), ntree); + + ED_node_tag_update_nodetree(CTX_data_main(C), ntree, node_to); +} + +/****************************** Node Link Menu *******************************/ + +// #define UI_NODE_LINK_ADD 0 +#define UI_NODE_LINK_DISCONNECT -1 +#define UI_NODE_LINK_REMOVE -2 + +struct NodeLinkArg { + Main *bmain; + Scene *scene; + bNodeTree *ntree; + bNode *node; + bNodeSocket *sock; + + bNodeType *node_type; + NodeLinkItem item; + + uiLayout *layout; +}; + +static void ui_node_link_items(NodeLinkArg *arg, + int in_out, + NodeLinkItem **r_items, + int *r_totitems) +{ + /* XXX this should become a callback for node types! */ + NodeLinkItem *items = nullptr; + int totitems = 0; + + if (arg->node_type->type == NODE_GROUP) { + bNodeTree *ngroup; + int i; + + for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; + ngroup = (bNodeTree *)ngroup->id.next) { + const char *disabled_hint; + if ((ngroup->type != arg->ntree->type) || + !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { + continue; + } + + ListBase *lb = ((in_out == SOCK_IN) ? &ngroup->inputs : &ngroup->outputs); + totitems += BLI_listbase_count(lb); + } + + if (totitems > 0) { + items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); + + i = 0; + for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; + ngroup = (bNodeTree *)ngroup->id.next) { + const char *disabled_hint; + if ((ngroup->type != arg->ntree->type) || + !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { + continue; + } + + ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs); + bNodeSocket *stemp; + int index; + for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; + stemp = stemp->next, index++, i++) { + NodeLinkItem *item = &items[i]; + + item->socket_index = index; + /* note: int stemp->type is not fully reliable, not used for node group + * interface sockets. use the typeinfo->type instead. + */ + item->socket_type = stemp->typeinfo->type; + item->socket_name = stemp->name; + item->node_name = ngroup->id.name + 2; + item->ngroup = ngroup; + } + } + } + } + else { + bNodeSocketTemplate *socket_templates = (in_out == SOCK_IN ? arg->node_type->inputs : + arg->node_type->outputs); + bNodeSocketTemplate *stemp; + int i; + + for (stemp = socket_templates; stemp && stemp->type != -1; stemp++) { + totitems++; + } + + if (totitems > 0) { + items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); + + i = 0; + for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) { + NodeLinkItem *item = &items[i]; + + item->socket_index = i; + item->socket_type = stemp->type; + item->socket_name = stemp->name; + item->node_name = arg->node_type->ui_name; + } + } + } + + *r_items = items; + *r_totitems = totitems; +} + +static void ui_node_link(bContext *C, void *arg_p, void *event_p) +{ + NodeLinkArg *arg = (NodeLinkArg *)arg_p; + Main *bmain = arg->bmain; + bNode *node_to = arg->node; + bNodeSocket *sock_to = arg->sock; + bNodeTree *ntree = arg->ntree; + int event = POINTER_AS_INT(event_p); + + if (event == UI_NODE_LINK_DISCONNECT) { + node_socket_disconnect(bmain, ntree, node_to, sock_to); + } + else if (event == UI_NODE_LINK_REMOVE) { + node_socket_remove(bmain, ntree, node_to, sock_to); + } + else { + node_socket_add_replace(C, ntree, node_to, sock_to, arg->node_type->type, &arg->item); + } + + ED_undo_push(C, "Node input modify"); +} + +static void ui_node_sock_name(bNodeTree *ntree, bNodeSocket *sock, char name[UI_MAX_NAME_STR]) +{ + if (sock->link && sock->link->fromnode) { + bNode *node = sock->link->fromnode; + char node_name[UI_MAX_NAME_STR]; + + nodeLabel(ntree, node, node_name, sizeof(node_name)); + + if (BLI_listbase_is_empty(&node->inputs) && node->outputs.first != node->outputs.last) { + BLI_snprintf( + name, UI_MAX_NAME_STR, "%s | %s", IFACE_(node_name), IFACE_(sock->link->fromsock->name)); + } + else { + BLI_strncpy(name, IFACE_(node_name), UI_MAX_NAME_STR); + } + } + else if (sock->type == SOCK_SHADER) { + BLI_strncpy(name, IFACE_("None"), UI_MAX_NAME_STR); + } + else { + BLI_strncpy(name, IFACE_("Default"), UI_MAX_NAME_STR); + } +} + +static int ui_compatible_sockets(int typeA, int typeB) +{ + return (typeA == typeB); +} + +static int ui_node_item_name_compare(const void *a, const void *b) +{ + const bNodeType *type_a = *(const bNodeType **)a; + const bNodeType *type_b = *(const bNodeType **)b; + return BLI_strcasecmp_natural(type_a->ui_name, type_b->ui_name); +} + +static bool ui_node_item_special_poll(const bNodeTree *UNUSED(ntree), const bNodeType *ntype) +{ + if (STREQ(ntype->idname, "ShaderNodeUVAlongStroke")) { + /* TODO(sergey): Currently we don't have Freestyle nodes edited from + * the buttons context, so can ignore its nodes completely. + * + * However, we might want to do some extra checks here later. + */ + return false; + } + return true; +} + +static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) +{ + bNodeTree *ntree = arg->ntree; + bNodeSocket *sock = arg->sock; + uiLayout *layout = arg->layout; + uiLayout *column = nullptr; + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but; + NodeLinkArg *argN; + int first = 1; + + /* generate array of node types sorted by UI name */ + blender::Vector sorted_ntypes; + + NODE_TYPES_BEGIN (ntype) { + const char *disabled_hint; + if (!(ntype->poll && ntype->poll(ntype, ntree, &disabled_hint))) { + continue; + } + + if (ntype->nclass != nclass) { + continue; + } + + if (!ui_node_item_special_poll(ntree, ntype)) { + continue; + } + + sorted_ntypes.append(ntype); + } + NODE_TYPES_END; + + qsort( + sorted_ntypes.data(), sorted_ntypes.size(), sizeof(bNodeType *), ui_node_item_name_compare); + + /* generate UI */ + for (int j = 0; j < sorted_ntypes.size(); j++) { + bNodeType *ntype = sorted_ntypes[j]; + NodeLinkItem *items; + int totitems; + char name[UI_MAX_NAME_STR]; + const char *cur_node_name = nullptr; + int num = 0; + int icon = ICON_NONE; + + arg->node_type = ntype; + + ui_node_link_items(arg, SOCK_OUT, &items, &totitems); + + for (int i = 0; i < totitems; i++) { + if (ui_compatible_sockets(items[i].socket_type, sock->type)) { + num++; + } + } + + for (int i = 0; i < totitems; i++) { + if (!ui_compatible_sockets(items[i].socket_type, sock->type)) { + continue; + } + + if (first) { + column = uiLayoutColumn(layout, false); + UI_block_layout_set_current(block, column); + + uiItemL(column, IFACE_(cname), ICON_NODE); + but = (uiBut *)block->buttons.last; + + first = 0; + } + + if (num > 1) { + if (!cur_node_name || !STREQ(cur_node_name, items[i].node_name)) { + cur_node_name = items[i].node_name; + /* XXX Do not use uiItemL here, + * it would add an empty icon as we are in a menu! */ + uiDefBut(block, + UI_BTYPE_LABEL, + 0, + IFACE_(cur_node_name), + 0, + 0, + UI_UNIT_X * 4, + UI_UNIT_Y, + nullptr, + 0.0, + 0.0, + 0.0, + 0.0, + ""); + } + + BLI_snprintf(name, UI_MAX_NAME_STR, "%s", IFACE_(items[i].socket_name)); + icon = ICON_BLANK1; + } + else { + BLI_strncpy(name, IFACE_(items[i].node_name), UI_MAX_NAME_STR); + icon = ICON_NONE; + } + + but = uiDefIconTextBut(block, + UI_BTYPE_BUT, + 0, + icon, + name, + 0, + 0, + UI_UNIT_X * 4, + UI_UNIT_Y, + nullptr, + 0.0, + 0.0, + 0.0, + 0.0, + TIP_("Add node to input")); + + argN = (NodeLinkArg *)MEM_dupallocN(arg); + argN->item = items[i]; + UI_but_funcN_set(but, ui_node_link, argN, nullptr); + } + + if (items) { + MEM_freeN(items); + } + } +} + +static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name) +{ + NodeLinkArg *arg = (NodeLinkArg *)calldata; + + if (!ELEM(nclass, NODE_CLASS_GROUP, NODE_CLASS_LAYOUT)) { + ui_node_menu_column(arg, nclass, name); + } +} + +static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but = (uiBut *)but_p; + uiLayout *split, *column; + NodeLinkArg *arg = (NodeLinkArg *)but->func_argN; + bNodeSocket *sock = arg->sock; + bNodeTreeType *ntreetype = arg->ntree->typeinfo; + + UI_block_flag_enable(block, UI_BLOCK_NO_FLIP | UI_BLOCK_IS_FLIP); + UI_block_layout_set_current(block, layout); + split = uiLayoutSplit(layout, 0.0f, false); + + arg->bmain = bmain; + arg->scene = scene; + arg->layout = split; + + if (ntreetype && ntreetype->foreach_nodeclass) { + ntreetype->foreach_nodeclass(scene, arg, node_menu_column_foreach_cb); + } + + column = uiLayoutColumn(split, false); + UI_block_layout_set_current(block, column); + + if (sock->link) { + uiItemL(column, IFACE_("Link"), ICON_NONE); + but = (uiBut *)block->buttons.last; + but->drawflag = UI_BUT_TEXT_LEFT; + + but = uiDefBut(block, + UI_BTYPE_BUT, + 0, + IFACE_("Remove"), + 0, + 0, + UI_UNIT_X * 4, + UI_UNIT_Y, + nullptr, + 0.0, + 0.0, + 0.0, + 0.0, + TIP_("Remove nodes connected to the input")); + UI_but_funcN_set(but, ui_node_link, MEM_dupallocN(arg), POINTER_FROM_INT(UI_NODE_LINK_REMOVE)); + + but = uiDefBut(block, + UI_BTYPE_BUT, + 0, + IFACE_("Disconnect"), + 0, + 0, + UI_UNIT_X * 4, + UI_UNIT_Y, + nullptr, + 0.0, + 0.0, + 0.0, + 0.0, + TIP_("Disconnect nodes connected to the input")); + UI_but_funcN_set( + but, ui_node_link, MEM_dupallocN(arg), POINTER_FROM_INT(UI_NODE_LINK_DISCONNECT)); + } + + ui_node_menu_column(arg, NODE_CLASS_GROUP, N_("Group")); +} + +void uiTemplateNodeLink( + uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input) +{ + uiBlock *block = uiLayoutGetBlock(layout); + NodeLinkArg *arg; + uiBut *but; + float socket_col[4]; + + arg = (NodeLinkArg *)MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg"); + arg->ntree = ntree; + arg->node = node; + arg->sock = input; + + PointerRNA node_ptr; + RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); + node_socket_color_get(C, ntree, &node_ptr, input, socket_col); + + UI_block_layout_set_current(block, layout); + + if (input->link || input->type == SOCK_SHADER || (input->flag & SOCK_HIDE_VALUE)) { + char name[UI_MAX_NAME_STR]; + ui_node_sock_name(ntree, input, name); + but = uiDefMenuBut( + block, ui_template_node_link_menu, nullptr, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, ""); + } + else { + but = uiDefIconMenuBut( + block, ui_template_node_link_menu, nullptr, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, ""); + } + + UI_but_type_set_menu_from_pulldown(but); + UI_but_node_link_set(but, input, socket_col); + UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); + + but->poin = (char *)but; + but->func_argN = arg; + + if (input->link && input->link->fromnode) { + if (input->link->fromnode->flag & NODE_ACTIVE_TEXTURE) { + but->flag |= UI_BUT_NODE_ACTIVE; + } + } +} + +/**************************** Node Tree Layout *******************************/ + +static void ui_node_draw_input( + uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth); + +static void ui_node_draw_node( + uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, int depth) +{ + bNodeSocket *input; + PointerRNA nodeptr; + + RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); + + if (node->typeinfo->draw_buttons) { + if (node->type != NODE_GROUP) { + uiLayoutSetPropSep(layout, true); + node->typeinfo->draw_buttons(layout, C, &nodeptr); + } + } + + for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) { + ui_node_draw_input(layout, C, ntree, node, input, depth + 1); + } +} + +static void ui_node_draw_input( + uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth) +{ + PointerRNA inputptr, nodeptr; + uiBlock *block = uiLayoutGetBlock(layout); + uiLayout *row = nullptr; + bNode *lnode; + bool dependency_loop; + + if (input->flag & SOCK_UNAVAIL) { + return; + } + + /* to avoid eternal loops on cyclic dependencies */ + node->flag |= NODE_TEST; + lnode = (input->link) ? input->link->fromnode : nullptr; + + dependency_loop = (lnode && (lnode->flag & NODE_TEST)); + if (dependency_loop) { + lnode = nullptr; + } + + /* socket RNA pointer */ + RNA_pointer_create(&ntree->id, &RNA_NodeSocket, input, &inputptr); + RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); + + row = uiLayoutRow(layout, true); + /* Decorations are added manually here. */ + uiLayoutSetPropDecorate(row, false); + + uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(row); + /* Empty decorator item for alignment. */ + bool add_dummy_decorator = false; + + { + uiLayout *sub = uiLayoutRow(split_wrapper.label_column, true); + + if (depth > 0) { + UI_block_emboss_set(block, UI_EMBOSS_NONE); + + if (lnode && + (lnode->inputs.first || (lnode->typeinfo->draw_buttons && lnode->type != NODE_GROUP))) { + int icon = (input->flag & SOCK_COLLAPSED) ? ICON_DISCLOSURE_TRI_RIGHT : + ICON_DISCLOSURE_TRI_DOWN; + uiItemR(sub, &inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon); + } + + UI_block_emboss_set(block, UI_EMBOSS); + } + + sub = uiLayoutRow(sub, true); + uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT); + uiItemL(sub, IFACE_(input->name), ICON_NONE); + } + + if (dependency_loop) { + uiItemL(row, IFACE_("Dependency Loop"), ICON_ERROR); + add_dummy_decorator = true; + } + else if (lnode) { + /* input linked to a node */ + uiTemplateNodeLink(row, C, ntree, node, input); + add_dummy_decorator = true; + + if (depth == 0 || !(input->flag & SOCK_COLLAPSED)) { + if (depth == 0) { + uiItemS(layout); + } + + ui_node_draw_node(layout, C, ntree, lnode, depth); + } + } + else { + row = uiLayoutRow(row, true); + + uiTemplateNodeLink(row, C, ntree, node, input); + + if (input->flag & SOCK_HIDE_VALUE) { + add_dummy_decorator = true; + } + /* input not linked, show value */ + else { + uiLayout *sub = row; + + switch (input->type) { + case SOCK_VECTOR: + if (input->type == SOCK_VECTOR) { + uiItemS(row); + sub = uiLayoutColumn(row, true); + } + ATTR_FALLTHROUGH; + case SOCK_FLOAT: + case SOCK_INT: + case SOCK_BOOLEAN: + case SOCK_RGBA: + uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE); + uiItemDecoratorR( + split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX); + break; + case SOCK_STRING: { + const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id; + if (node_tree->type == NTREE_GEOMETRY) { + node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, row); + } + else { + uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE); + } + uiItemDecoratorR( + split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX); + break; + } + default: + add_dummy_decorator = true; + } + } + } + + if (add_dummy_decorator) { + uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0); + } + + /* clear */ + node->flag &= ~NODE_TEST; +} + +void uiTemplateNodeView( + uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input) +{ + bNode *tnode; + + if (!ntree) { + return; + } + + /* clear for cycle check */ + for (tnode = (bNode *)ntree->nodes.first; tnode; tnode = tnode->next) { + tnode->flag &= ~NODE_TEST; + } + + if (input) { + ui_node_draw_input(layout, C, ntree, node, input, 0); + } + else { + ui_node_draw_node(layout, C, ntree, node, 0); + } +} diff --git a/source/blender/editors/space_node/node_toolbar.c b/source/blender/editors/space_node/node_toolbar.c deleted file mode 100644 index 2e7d6ab6cd5..00000000000 --- a/source/blender/editors/space_node/node_toolbar.c +++ /dev/null @@ -1,39 +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) 2012 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup nodes - */ - -#include "BLI_utildefines.h" - -#include "DNA_node_types.h" - -#include "BKE_context.h" -#include "BKE_screen.h" - -#include "WM_api.h" - -#include "node_intern.h" /* own include */ - -/* ******************* node toolbar registration ************** */ - -void node_toolbar_register(ARegionType *UNUSED(art)) -{ -} diff --git a/source/blender/editors/space_node/node_toolbar.cc b/source/blender/editors/space_node/node_toolbar.cc new file mode 100644 index 00000000000..2e7d6ab6cd5 --- /dev/null +++ b/source/blender/editors/space_node/node_toolbar.cc @@ -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. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup nodes + */ + +#include "BLI_utildefines.h" + +#include "DNA_node_types.h" + +#include "BKE_context.h" +#include "BKE_screen.h" + +#include "WM_api.h" + +#include "node_intern.h" /* own include */ + +/* ******************* node toolbar registration ************** */ + +void node_toolbar_register(ARegionType *UNUSED(art)) +{ +} diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c deleted file mode 100644 index 8ecab92aa26..00000000000 --- a/source/blender/editors/space_node/node_view.c +++ /dev/null @@ -1,700 +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) 2008 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup spnode - */ - -#include "DNA_node_types.h" - -#include "BLI_math.h" -#include "BLI_rect.h" -#include "BLI_utildefines.h" - -#include "BKE_context.h" -#include "BKE_image.h" -#include "BKE_main.h" -#include "BKE_node.h" -#include "BKE_screen.h" - -#include "ED_image.h" -#include "ED_node.h" /* own include */ -#include "ED_screen.h" -#include "ED_space_api.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "UI_view2d.h" - -#include "MEM_guardedalloc.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "node_intern.h" /* own include */ - -/* -------------------------------------------------------------------- */ -/** \name View All Operator - * \{ */ - -int space_node_view_flag( - bContext *C, SpaceNode *snode, ARegion *region, const int node_flag, const int smooth_viewtx) -{ - bNode *node; - rctf cur_new; - float oldwidth, oldheight, width, height; - float oldasp, asp; - int tot = 0; - bool has_frame = false; - - oldwidth = BLI_rctf_size_x(®ion->v2d.cur); - oldheight = BLI_rctf_size_y(®ion->v2d.cur); - - oldasp = oldwidth / oldheight; - - BLI_rctf_init_minmax(&cur_new); - - if (snode->edittree) { - for (node = snode->edittree->nodes.first; node; node = node->next) { - if ((node->flag & node_flag) == node_flag) { - BLI_rctf_union(&cur_new, &node->totr); - tot++; - - if (node->type == NODE_FRAME) { - has_frame = true; - } - } - } - } - - if (tot) { - width = BLI_rctf_size_x(&cur_new); - height = BLI_rctf_size_y(&cur_new); - asp = width / height; - - /* for single non-frame nodes, don't zoom in, just pan view, - * but do allow zooming out, this allows for big nodes to be zoomed out */ - if ((tot == 1) && (has_frame == false) && ((oldwidth * oldheight) > (width * height))) { - /* center, don't zoom */ - BLI_rctf_resize(&cur_new, oldwidth, oldheight); - } - else { - if (oldasp < asp) { - const float height_new = width / oldasp; - cur_new.ymin = cur_new.ymin - height_new / 2.0f; - cur_new.ymax = cur_new.ymax + height_new / 2.0f; - } - else { - const float width_new = height * oldasp; - cur_new.xmin = cur_new.xmin - width_new / 2.0f; - cur_new.xmax = cur_new.xmax + width_new / 2.0f; - } - - /* add some padding */ - BLI_rctf_scale(&cur_new, 1.1f); - } - - UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx); - } - - return (tot != 0); -} - -static int node_view_all_exec(bContext *C, wmOperator *op) -{ - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - /* is this really needed? */ - snode->xof = 0; - snode->yof = 0; - - if (space_node_view_flag(C, snode, region, 0, smooth_viewtx)) { - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void NODE_OT_view_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Frame All"; - ot->idname = "NODE_OT_view_all"; - ot->description = "Resize view so you can see all nodes"; - - /* api callbacks */ - ot->exec = node_view_all_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = 0; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name View Selected Operator - * \{ */ - -static int node_view_selected_exec(bContext *C, wmOperator *op) -{ - ARegion *region = CTX_wm_region(C); - SpaceNode *snode = CTX_wm_space_node(C); - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - - if (space_node_view_flag(C, snode, region, NODE_SELECT, smooth_viewtx)) { - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void NODE_OT_view_selected(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Frame Selected"; - ot->idname = "NODE_OT_view_selected"; - ot->description = "Resize view so you can see selected nodes"; - - /* api callbacks */ - ot->exec = node_view_selected_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = 0; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Background Image Operators - * \{ */ - -typedef struct NodeViewMove { - int mvalo[2]; - int xmin, ymin, xmax, ymax; -} NodeViewMove; - -static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - NodeViewMove *nvm = op->customdata; - - switch (event->type) { - case MOUSEMOVE: - - snode->xof -= (nvm->mvalo[0] - event->mval[0]); - snode->yof -= (nvm->mvalo[1] - event->mval[1]); - nvm->mvalo[0] = event->mval[0]; - nvm->mvalo[1] = event->mval[1]; - - /* prevent dragging image outside of the window and losing it! */ - CLAMP(snode->xof, nvm->xmin, nvm->xmax); - CLAMP(snode->yof, nvm->ymin, nvm->ymax); - - ED_region_tag_redraw(region); - WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL); - WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL); - - break; - - case LEFTMOUSE: - case MIDDLEMOUSE: - case RIGHTMOUSE: - if (event->val == KM_RELEASE) { - MEM_freeN(nvm); - op->customdata = NULL; - return OPERATOR_FINISHED; - } - break; - } - - return OPERATOR_RUNNING_MODAL; -} - -static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - NodeViewMove *nvm; - Image *ima; - ImBuf *ibuf; - const float pad = 32.0f; /* better be bigger than scrollbars */ - - void *lock; - - ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); - - if (ibuf == NULL) { - BKE_image_release_ibuf(ima, ibuf, lock); - return OPERATOR_CANCELLED; - } - - nvm = MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct"); - op->customdata = nvm; - nvm->mvalo[0] = event->mval[0]; - nvm->mvalo[1] = event->mval[1]; - - nvm->xmin = -(region->winx / 2) - (ibuf->x * (0.5f * snode->zoom)) + pad; - nvm->xmax = (region->winx / 2) + (ibuf->x * (0.5f * snode->zoom)) - pad; - nvm->ymin = -(region->winy / 2) - (ibuf->y * (0.5f * snode->zoom)) + pad; - nvm->ymax = (region->winy / 2) + (ibuf->y * (0.5f * snode->zoom)) - pad; - - BKE_image_release_ibuf(ima, ibuf, lock); - - /* add modal handler */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static void snode_bg_viewmove_cancel(bContext *UNUSED(C), wmOperator *op) -{ - MEM_freeN(op->customdata); - op->customdata = NULL; -} - -void NODE_OT_backimage_move(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Background Image Move"; - ot->description = "Move node backdrop"; - ot->idname = "NODE_OT_backimage_move"; - - /* api callbacks */ - ot->invoke = snode_bg_viewmove_invoke; - ot->modal = snode_bg_viewmove_modal; - ot->poll = composite_node_active; - ot->cancel = snode_bg_viewmove_cancel; - - /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Background Image Zoom - * \{ */ - -static int backimage_zoom_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - float fac = RNA_float_get(op->ptr, "factor"); - - snode->zoom *= fac; - ED_region_tag_redraw(region); - WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL); - WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_backimage_zoom(wmOperatorType *ot) -{ - - /* identifiers */ - ot->name = "Background Image Zoom"; - ot->idname = "NODE_OT_backimage_zoom"; - ot->description = "Zoom in/out the background image"; - - /* api callbacks */ - ot->exec = backimage_zoom_exec; - ot->poll = composite_node_active; - - /* flags */ - ot->flag = OPTYPE_BLOCKING; - - /* internal */ - RNA_def_float(ot->srna, "factor", 1.2f, 0.0f, 10.0f, "Factor", "", 0.0f, 10.0f); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Background Image Fit - * \{ */ - -static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - - Image *ima; - ImBuf *ibuf; - - const float pad = 32.0f; - - void *lock; - - float facx, facy; - - ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); - - if ((ibuf == NULL) || (ibuf->x == 0) || (ibuf->y == 0)) { - BKE_image_release_ibuf(ima, ibuf, lock); - return OPERATOR_CANCELLED; - } - - facx = 1.0f * (region->sizex - pad) / (ibuf->x * snode->zoom); - facy = 1.0f * (region->sizey - pad) / (ibuf->y * snode->zoom); - - BKE_image_release_ibuf(ima, ibuf, lock); - - snode->zoom *= min_ff(facx, facy) * U.dpi_fac; - - snode->xof = 0; - snode->yof = 0; - - ED_region_tag_redraw(region); - WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL); - WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL); - - return OPERATOR_FINISHED; -} - -void NODE_OT_backimage_fit(wmOperatorType *ot) -{ - - /* identifiers */ - ot->name = "Background Image Fit"; - ot->idname = "NODE_OT_backimage_fit"; - ot->description = "Fit the background image to the view"; - - /* api callbacks */ - ot->exec = backimage_fit_exec; - ot->poll = composite_node_active; - - /* flags */ - ot->flag = OPTYPE_BLOCKING; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Sample Backdrop Operator - * \{ */ - -typedef struct ImageSampleInfo { - ARegionType *art; - void *draw_handle; - int x, y; - int channels; - - uchar col[4]; - float colf[4]; - float linearcol[4]; - - int z; - float zf; - - int *zp; - float *zfp; - - int draw; - int color_manage; -} ImageSampleInfo; - -static void sample_draw(const bContext *C, ARegion *region, void *arg_info) -{ - Scene *scene = CTX_data_scene(C); - ImageSampleInfo *info = arg_info; - - if (info->draw) { - ED_image_draw_info(scene, - region, - info->color_manage, - false, - info->channels, - info->x, - info->y, - info->col, - info->colf, - info->linearcol, - info->zp, - info->zfp); - } -} - -/* Returns mouse position in image space. */ -bool ED_space_node_get_position( - Main *bmain, SpaceNode *snode, struct ARegion *ar, const int mval[2], float fpos[2]) -{ - if (!ED_node_is_compositor(snode) || (snode->flag & SNODE_BACKDRAW) == 0) { - return false; - } - - void *lock; - Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); - if (!ibuf) { - BKE_image_release_ibuf(ima, ibuf, lock); - return false; - } - - /* map the mouse coords to the backdrop image space */ - float bufx = ibuf->x * snode->zoom; - float bufy = ibuf->y * snode->zoom; - fpos[0] = (bufx > 0.0f ? ((float)mval[0] - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f); - fpos[1] = (bufy > 0.0f ? ((float)mval[1] - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f); - - BKE_image_release_ibuf(ima, ibuf, lock); - return true; -} - -/* Returns color in linear space, matching ED_space_image_color_sample(). - * And here we've got recursion in the comments tips... - */ -bool ED_space_node_color_sample( - Main *bmain, SpaceNode *snode, ARegion *region, const int mval[2], float r_col[3]) -{ - void *lock; - Image *ima; - ImBuf *ibuf; - float fx, fy, bufx, bufy; - bool ret = false; - - if (!ED_node_is_compositor(snode) || (snode->flag & SNODE_BACKDRAW) == 0) { - /* use viewer image for color sampling only if we're in compositor tree - * with backdrop enabled - */ - return false; - } - - ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); - if (!ibuf) { - return false; - } - - /* map the mouse coords to the backdrop image space */ - bufx = ibuf->x * snode->zoom; - bufy = ibuf->y * snode->zoom; - fx = (bufx > 0.0f ? ((float)mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f); - fy = (bufy > 0.0f ? ((float)mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f); - - if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) { - const float *fp; - uchar *cp; - int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y); - - CLAMP(x, 0, ibuf->x - 1); - CLAMP(y, 0, ibuf->y - 1); - - if (ibuf->rect_float) { - fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); - /* #IB_PROFILE_NONE is default but in fact its linear. */ - copy_v3_v3(r_col, fp); - ret = true; - } - else if (ibuf->rect) { - cp = (uchar *)(ibuf->rect + y * ibuf->x + x); - rgb_uchar_to_float(r_col, cp); - IMB_colormanagement_colorspace_to_scene_linear_v3(r_col, ibuf->rect_colorspace); - ret = true; - } - } - - BKE_image_release_ibuf(ima, ibuf, lock); - - return ret; -} - -static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) -{ - Main *bmain = CTX_data_main(C); - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - ImageSampleInfo *info = op->customdata; - void *lock; - Image *ima; - ImBuf *ibuf; - float fx, fy, bufx, bufy; - - ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); - if (!ibuf) { - info->draw = 0; - return; - } - - if (!ibuf->rect) { - IMB_rect_from_float(ibuf); - } - - /* map the mouse coords to the backdrop image space */ - bufx = ibuf->x * snode->zoom; - bufy = ibuf->y * snode->zoom; - fx = (bufx > 0.0f ? ((float)event->mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f : - 0.0f); - fy = (bufy > 0.0f ? ((float)event->mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f : - 0.0f); - - if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) { - const float *fp; - uchar *cp; - int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y); - - CLAMP(x, 0, ibuf->x - 1); - CLAMP(y, 0, ibuf->y - 1); - - info->x = x; - info->y = y; - info->draw = 1; - info->channels = ibuf->channels; - - info->zp = NULL; - info->zfp = NULL; - - if (ibuf->rect) { - cp = (uchar *)(ibuf->rect + y * ibuf->x + x); - - info->col[0] = cp[0]; - info->col[1] = cp[1]; - info->col[2] = cp[2]; - info->col[3] = cp[3]; - - info->colf[0] = (float)cp[0] / 255.0f; - info->colf[1] = (float)cp[1] / 255.0f; - info->colf[2] = (float)cp[2] / 255.0f; - info->colf[3] = (float)cp[3] / 255.0f; - - copy_v4_v4(info->linearcol, info->colf); - IMB_colormanagement_colorspace_to_scene_linear_v4( - info->linearcol, false, ibuf->rect_colorspace); - - info->color_manage = true; - } - if (ibuf->rect_float) { - fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); - - info->colf[0] = fp[0]; - info->colf[1] = fp[1]; - info->colf[2] = fp[2]; - info->colf[3] = fp[3]; - - info->color_manage = true; - } - - if (ibuf->zbuf) { - info->z = ibuf->zbuf[y * ibuf->x + x]; - info->zp = &info->z; - } - if (ibuf->zbuf_float) { - info->zf = ibuf->zbuf_float[y * ibuf->x + x]; - info->zfp = &info->zf; - } - - ED_node_sample_set(info->colf); - } - else { - info->draw = 0; - ED_node_sample_set(NULL); - } - - BKE_image_release_ibuf(ima, ibuf, lock); - - ED_area_tag_redraw(CTX_wm_area(C)); -} - -static void sample_exit(bContext *C, wmOperator *op) -{ - ImageSampleInfo *info = op->customdata; - - ED_node_sample_set(NULL); - ED_region_draw_cb_exit(info->art, info->draw_handle); - ED_area_tag_redraw(CTX_wm_area(C)); - MEM_freeN(info); -} - -static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - SpaceNode *snode = CTX_wm_space_node(C); - ARegion *region = CTX_wm_region(C); - ImageSampleInfo *info; - - if (!ED_node_is_compositor(snode) || !(snode->flag & SNODE_BACKDRAW)) { - return OPERATOR_CANCELLED; - } - - info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); - info->art = region->type; - info->draw_handle = ED_region_draw_cb_activate( - region->type, sample_draw, info, REGION_DRAW_POST_PIXEL); - op->customdata = info; - - sample_apply(C, op, event); - - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static int sample_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - switch (event->type) { - case LEFTMOUSE: - case RIGHTMOUSE: /* XXX hardcoded */ - if (event->val == KM_RELEASE) { - sample_exit(C, op); - return OPERATOR_CANCELLED; - } - break; - case MOUSEMOVE: - sample_apply(C, op, event); - break; - } - - return OPERATOR_RUNNING_MODAL; -} - -static void sample_cancel(bContext *C, wmOperator *op) -{ - sample_exit(C, op); -} - -void NODE_OT_backimage_sample(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Backimage Sample"; - ot->idname = "NODE_OT_backimage_sample"; - ot->description = "Use mouse to sample background image"; - - /* api callbacks */ - ot->invoke = sample_invoke; - ot->modal = sample_modal; - ot->cancel = sample_cancel; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_BLOCKING; -} - -/** \} */ diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc new file mode 100644 index 00000000000..f0db0539c4f --- /dev/null +++ b/source/blender/editors/space_node/node_view.cc @@ -0,0 +1,702 @@ +/* + * 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) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spnode + */ + +#include "DNA_node_types.h" + +#include "BLI_math.h" +#include "BLI_rect.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_screen.h" + +#include "ED_image.h" +#include "ED_node.h" /* own include */ +#include "ED_screen.h" +#include "ED_space_api.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_view2d.h" + +#include "MEM_guardedalloc.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "node_intern.h" /* own include */ + +/* -------------------------------------------------------------------- */ +/** \name View All Operator + * \{ */ + +int space_node_view_flag( + bContext *C, SpaceNode *snode, ARegion *region, const int node_flag, const int smooth_viewtx) +{ + bNode *node; + rctf cur_new; + float oldwidth, oldheight, width, height; + float oldasp, asp; + int tot = 0; + bool has_frame = false; + + oldwidth = BLI_rctf_size_x(®ion->v2d.cur); + oldheight = BLI_rctf_size_y(®ion->v2d.cur); + + oldasp = oldwidth / oldheight; + + BLI_rctf_init_minmax(&cur_new); + + if (snode->edittree) { + for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { + if ((node->flag & node_flag) == node_flag) { + BLI_rctf_union(&cur_new, &node->totr); + tot++; + + if (node->type == NODE_FRAME) { + has_frame = true; + } + } + } + } + + if (tot) { + width = BLI_rctf_size_x(&cur_new); + height = BLI_rctf_size_y(&cur_new); + asp = width / height; + + /* for single non-frame nodes, don't zoom in, just pan view, + * but do allow zooming out, this allows for big nodes to be zoomed out */ + if ((tot == 1) && (has_frame == false) && ((oldwidth * oldheight) > (width * height))) { + /* center, don't zoom */ + BLI_rctf_resize(&cur_new, oldwidth, oldheight); + } + else { + if (oldasp < asp) { + const float height_new = width / oldasp; + cur_new.ymin = cur_new.ymin - height_new / 2.0f; + cur_new.ymax = cur_new.ymax + height_new / 2.0f; + } + else { + const float width_new = height * oldasp; + cur_new.xmin = cur_new.xmin - width_new / 2.0f; + cur_new.xmax = cur_new.xmax + width_new / 2.0f; + } + + /* add some padding */ + BLI_rctf_scale(&cur_new, 1.1f); + } + + UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx); + } + + return (tot != 0); +} + +static int node_view_all_exec(bContext *C, wmOperator *op) +{ + ARegion *region = CTX_wm_region(C); + SpaceNode *snode = CTX_wm_space_node(C); + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + /* is this really needed? */ + snode->xof = 0; + snode->yof = 0; + + if (space_node_view_flag(C, snode, region, 0, smooth_viewtx)) { + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +void NODE_OT_view_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Frame All"; + ot->idname = "NODE_OT_view_all"; + ot->description = "Resize view so you can see all nodes"; + + /* api callbacks */ + ot->exec = node_view_all_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Selected Operator + * \{ */ + +static int node_view_selected_exec(bContext *C, wmOperator *op) +{ + ARegion *region = CTX_wm_region(C); + SpaceNode *snode = CTX_wm_space_node(C); + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + + if (space_node_view_flag(C, snode, region, NODE_SELECT, smooth_viewtx)) { + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +void NODE_OT_view_selected(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Frame Selected"; + ot->idname = "NODE_OT_view_selected"; + ot->description = "Resize view so you can see selected nodes"; + + /* api callbacks */ + ot->exec = node_view_selected_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = 0; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Background Image Operators + * \{ */ + +struct NodeViewMove { + int mvalo[2]; + int xmin, ymin, xmax, ymax; +}; + +static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + NodeViewMove *nvm = (NodeViewMove *)op->customdata; + + switch (event->type) { + case MOUSEMOVE: + + snode->xof -= (nvm->mvalo[0] - event->mval[0]); + snode->yof -= (nvm->mvalo[1] - event->mval[1]); + nvm->mvalo[0] = event->mval[0]; + nvm->mvalo[1] = event->mval[1]; + + /* prevent dragging image outside of the window and losing it! */ + CLAMP(snode->xof, nvm->xmin, nvm->xmax); + CLAMP(snode->yof, nvm->ymin, nvm->ymax); + + ED_region_tag_redraw(region); + WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr); + WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr); + + break; + + case LEFTMOUSE: + case MIDDLEMOUSE: + case RIGHTMOUSE: + if (event->val == KM_RELEASE) { + MEM_freeN(nvm); + op->customdata = nullptr; + return OPERATOR_FINISHED; + } + break; + } + + return OPERATOR_RUNNING_MODAL; +} + +static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + NodeViewMove *nvm; + Image *ima; + ImBuf *ibuf; + const float pad = 32.0f; /* better be bigger than scrollbars */ + + void *lock; + + ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); + + if (ibuf == nullptr) { + BKE_image_release_ibuf(ima, ibuf, lock); + return OPERATOR_CANCELLED; + } + + nvm = (NodeViewMove *)MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct"); + op->customdata = nvm; + nvm->mvalo[0] = event->mval[0]; + nvm->mvalo[1] = event->mval[1]; + + nvm->xmin = -(region->winx / 2) - (ibuf->x * (0.5f * snode->zoom)) + pad; + nvm->xmax = (region->winx / 2) + (ibuf->x * (0.5f * snode->zoom)) - pad; + nvm->ymin = -(region->winy / 2) - (ibuf->y * (0.5f * snode->zoom)) + pad; + nvm->ymax = (region->winy / 2) + (ibuf->y * (0.5f * snode->zoom)) - pad; + + BKE_image_release_ibuf(ima, ibuf, lock); + + /* add modal handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static void snode_bg_viewmove_cancel(bContext *UNUSED(C), wmOperator *op) +{ + MEM_freeN(op->customdata); + op->customdata = nullptr; +} + +void NODE_OT_backimage_move(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Background Image Move"; + ot->description = "Move node backdrop"; + ot->idname = "NODE_OT_backimage_move"; + + /* api callbacks */ + ot->invoke = snode_bg_viewmove_invoke; + ot->modal = snode_bg_viewmove_modal; + ot->poll = composite_node_active; + ot->cancel = snode_bg_viewmove_cancel; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Background Image Zoom + * \{ */ + +static int backimage_zoom_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + float fac = RNA_float_get(op->ptr, "factor"); + + snode->zoom *= fac; + ED_region_tag_redraw(region); + WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr); + WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_backimage_zoom(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Background Image Zoom"; + ot->idname = "NODE_OT_backimage_zoom"; + ot->description = "Zoom in/out the background image"; + + /* api callbacks */ + ot->exec = backimage_zoom_exec; + ot->poll = composite_node_active; + + /* flags */ + ot->flag = OPTYPE_BLOCKING; + + /* internal */ + RNA_def_float(ot->srna, "factor", 1.2f, 0.0f, 10.0f, "Factor", "", 0.0f, 10.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Background Image Fit + * \{ */ + +static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + + Image *ima; + ImBuf *ibuf; + + const float pad = 32.0f; + + void *lock; + + float facx, facy; + + ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); + + if ((ibuf == nullptr) || (ibuf->x == 0) || (ibuf->y == 0)) { + BKE_image_release_ibuf(ima, ibuf, lock); + return OPERATOR_CANCELLED; + } + + facx = 1.0f * (region->sizex - pad) / (ibuf->x * snode->zoom); + facy = 1.0f * (region->sizey - pad) / (ibuf->y * snode->zoom); + + BKE_image_release_ibuf(ima, ibuf, lock); + + snode->zoom *= min_ff(facx, facy) * U.dpi_fac; + + snode->xof = 0; + snode->yof = 0; + + ED_region_tag_redraw(region); + WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr); + WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_backimage_fit(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Background Image Fit"; + ot->idname = "NODE_OT_backimage_fit"; + ot->description = "Fit the background image to the view"; + + /* api callbacks */ + ot->exec = backimage_fit_exec; + ot->poll = composite_node_active; + + /* flags */ + ot->flag = OPTYPE_BLOCKING; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sample Backdrop Operator + * \{ */ + +struct ImageSampleInfo { + ARegionType *art; + void *draw_handle; + int x, y; + int channels; + + uchar col[4]; + float colf[4]; + float linearcol[4]; + + int z; + float zf; + + int *zp; + float *zfp; + + int draw; + int color_manage; +}; + +static void sample_draw(const bContext *C, ARegion *region, void *arg_info) +{ + Scene *scene = CTX_data_scene(C); + ImageSampleInfo *info = (ImageSampleInfo *)arg_info; + + if (info->draw) { + ED_image_draw_info(scene, + region, + info->color_manage, + false, + info->channels, + info->x, + info->y, + info->col, + info->colf, + info->linearcol, + info->zp, + info->zfp); + } +} + +/* Returns mouse position in image space. */ +bool ED_space_node_get_position( + Main *bmain, SpaceNode *snode, struct ARegion *region, const int mval[2], float fpos[2]) +{ + if (!ED_node_is_compositor(snode) || (snode->flag & SNODE_BACKDRAW) == 0) { + return false; + } + + void *lock; + Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); + if (!ibuf) { + BKE_image_release_ibuf(ima, ibuf, lock); + return false; + } + + /* map the mouse coords to the backdrop image space */ + float bufx = ibuf->x * snode->zoom; + float bufy = ibuf->y * snode->zoom; + fpos[0] = (bufx > 0.0f ? ((float)mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f : + 0.0f); + fpos[1] = (bufy > 0.0f ? ((float)mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f : + 0.0f); + + BKE_image_release_ibuf(ima, ibuf, lock); + return true; +} + +/* Returns color in linear space, matching ED_space_image_color_sample(). + * And here we've got recursion in the comments tips... + */ +bool ED_space_node_color_sample( + Main *bmain, SpaceNode *snode, ARegion *region, const int mval[2], float r_col[3]) +{ + void *lock; + Image *ima; + ImBuf *ibuf; + float fx, fy, bufx, bufy; + bool ret = false; + + if (!ED_node_is_compositor(snode) || (snode->flag & SNODE_BACKDRAW) == 0) { + /* use viewer image for color sampling only if we're in compositor tree + * with backdrop enabled + */ + return false; + } + + ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); + if (!ibuf) { + return false; + } + + /* map the mouse coords to the backdrop image space */ + bufx = ibuf->x * snode->zoom; + bufy = ibuf->y * snode->zoom; + fx = (bufx > 0.0f ? ((float)mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f); + fy = (bufy > 0.0f ? ((float)mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f); + + if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) { + const float *fp; + uchar *cp; + int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y); + + CLAMP(x, 0, ibuf->x - 1); + CLAMP(y, 0, ibuf->y - 1); + + if (ibuf->rect_float) { + fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); + /* #IB_PROFILE_NONE is default but in fact its linear. */ + copy_v3_v3(r_col, fp); + ret = true; + } + else if (ibuf->rect) { + cp = (uchar *)(ibuf->rect + y * ibuf->x + x); + rgb_uchar_to_float(r_col, cp); + IMB_colormanagement_colorspace_to_scene_linear_v3(r_col, ibuf->rect_colorspace); + ret = true; + } + } + + BKE_image_release_ibuf(ima, ibuf, lock); + + return ret; +} + +static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + ImageSampleInfo *info = (ImageSampleInfo *)op->customdata; + void *lock; + Image *ima; + ImBuf *ibuf; + float fx, fy, bufx, bufy; + + ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); + if (!ibuf) { + info->draw = 0; + return; + } + + if (!ibuf->rect) { + IMB_rect_from_float(ibuf); + } + + /* map the mouse coords to the backdrop image space */ + bufx = ibuf->x * snode->zoom; + bufy = ibuf->y * snode->zoom; + fx = (bufx > 0.0f ? ((float)event->mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f : + 0.0f); + fy = (bufy > 0.0f ? ((float)event->mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f : + 0.0f); + + if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) { + const float *fp; + uchar *cp; + int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y); + + CLAMP(x, 0, ibuf->x - 1); + CLAMP(y, 0, ibuf->y - 1); + + info->x = x; + info->y = y; + info->draw = 1; + info->channels = ibuf->channels; + + info->zp = nullptr; + info->zfp = nullptr; + + if (ibuf->rect) { + cp = (uchar *)(ibuf->rect + y * ibuf->x + x); + + info->col[0] = cp[0]; + info->col[1] = cp[1]; + info->col[2] = cp[2]; + info->col[3] = cp[3]; + + info->colf[0] = (float)cp[0] / 255.0f; + info->colf[1] = (float)cp[1] / 255.0f; + info->colf[2] = (float)cp[2] / 255.0f; + info->colf[3] = (float)cp[3] / 255.0f; + + copy_v4_v4(info->linearcol, info->colf); + IMB_colormanagement_colorspace_to_scene_linear_v4( + info->linearcol, false, ibuf->rect_colorspace); + + info->color_manage = true; + } + if (ibuf->rect_float) { + fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); + + info->colf[0] = fp[0]; + info->colf[1] = fp[1]; + info->colf[2] = fp[2]; + info->colf[3] = fp[3]; + + info->color_manage = true; + } + + if (ibuf->zbuf) { + info->z = ibuf->zbuf[y * ibuf->x + x]; + info->zp = &info->z; + } + if (ibuf->zbuf_float) { + info->zf = ibuf->zbuf_float[y * ibuf->x + x]; + info->zfp = &info->zf; + } + + ED_node_sample_set(info->colf); + } + else { + info->draw = 0; + ED_node_sample_set(nullptr); + } + + BKE_image_release_ibuf(ima, ibuf, lock); + + ED_area_tag_redraw(CTX_wm_area(C)); +} + +static void sample_exit(bContext *C, wmOperator *op) +{ + ImageSampleInfo *info = (ImageSampleInfo *)op->customdata; + + ED_node_sample_set(nullptr); + ED_region_draw_cb_exit(info->art, info->draw_handle); + ED_area_tag_redraw(CTX_wm_area(C)); + MEM_freeN(info); +} + +static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + SpaceNode *snode = CTX_wm_space_node(C); + ARegion *region = CTX_wm_region(C); + ImageSampleInfo *info; + + if (!ED_node_is_compositor(snode) || !(snode->flag & SNODE_BACKDRAW)) { + return OPERATOR_CANCELLED; + } + + info = (ImageSampleInfo *)MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); + info->art = region->type; + info->draw_handle = ED_region_draw_cb_activate( + region->type, sample_draw, info, REGION_DRAW_POST_PIXEL); + op->customdata = info; + + sample_apply(C, op, event); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int sample_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + switch (event->type) { + case LEFTMOUSE: + case RIGHTMOUSE: /* XXX hardcoded */ + if (event->val == KM_RELEASE) { + sample_exit(C, op); + return OPERATOR_CANCELLED; + } + break; + case MOUSEMOVE: + sample_apply(C, op, event); + break; + } + + return OPERATOR_RUNNING_MODAL; +} + +static void sample_cancel(bContext *C, wmOperator *op) +{ + sample_exit(C, op); +} + +void NODE_OT_backimage_sample(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Backimage Sample"; + ot->idname = "NODE_OT_backimage_sample"; + ot->description = "Use mouse to sample background image"; + + /* api callbacks */ + ot->invoke = sample_invoke; + ot->modal = sample_modal; + ot->cancel = sample_cancel; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_BLOCKING; +} + +/** \} */ -- cgit v1.2.3 From dc960a81d1bdc8c91a6967f85d1943896b06dc47 Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Wed, 2 Jun 2021 11:18:00 -0700 Subject: Boolean exact: speedup when there are many components. When there are many components (separate pieces of connected mesh), a part of the algorithm to determine component containment was slow. Using a float version of finding the nearest point on a triangle as a prefilter sped this up enormously. A case of 25 icospheres subdivided twice goes 11 seconds faster on my Macbook pro with this change. --- source/blender/blenlib/intern/mesh_boolean.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index cc27e9238c3..f74c17d6ee1 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -1829,6 +1829,19 @@ static mpq_class closest_on_tri_to_point(const mpq3 &p, return mpq3::distance_squared_with_buffer(p, r, m); } +static float closest_on_tri_to_point_float_dist_squared(const float3 &p, + const double3 &a, + const double3 &b, + const double3 &c) +{ + float3 fa, fb, fc, closest; + copy_v3fl_v3db(fa, a); + copy_v3fl_v3db(fb, b); + copy_v3fl_v3db(fc, c); + closest_on_tri_to_point_v3(closest, p, fa, fb, fc); + return len_squared_v3v3(p, closest); +} + struct ComponentContainer { int containing_component{NO_INDEX}; int nearest_cell{NO_INDEX}; @@ -1865,6 +1878,8 @@ static Vector find_component_containers(int comp, if (dbg_level > 0) { std::cout << "test vertex in comp: " << test_v << "\n"; } + const double3 &test_v_d = test_v->co; + float3 test_v_f(test_v_d[0], test_v_d[1], test_v_d[2]); mpq3 buf[7]; @@ -1879,6 +1894,7 @@ static Vector find_component_containers(int comp, int nearest_tri_close_vert = -1; int nearest_tri_close_edge = -1; mpq_class nearest_tri_dist_squared; + float nearest_tri_dist_squared_float = FLT_MAX; for (int p : components[comp_other]) { const Patch &patch = pinfo.patch(p); for (int t : patch.tris()) { @@ -1888,6 +1904,12 @@ static Vector find_component_containers(int comp, } int close_vert; int close_edge; + /* Try a cheap float test first. */ + float d2_f = closest_on_tri_to_point_float_dist_squared( + test_v_f, tri[0]->co, tri[1]->co, tri[2]->co); + if (d2_f - FLT_EPSILON > nearest_tri_dist_squared_float) { + continue; + } mpq_class d2 = closest_on_tri_to_point(test_v->co_exact, tri[0]->co_exact, tri[1]->co_exact, @@ -1910,6 +1932,7 @@ static Vector find_component_containers(int comp, nearest_tri_close_edge = close_edge; nearest_tri_close_vert = close_vert; nearest_tri_dist_squared = d2; + nearest_tri_dist_squared_float = d2_f; } } } -- cgit v1.2.3 From 81366b7d2c548f40843c7f87842710e3a2019136 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Wed, 2 Jun 2021 12:04:21 -0700 Subject: Boolean exact: speedup by parallelizing a plane calculation. This patch is from erik85, who says: This patch makes populate_plane inside polymesh_from_trimesh_with_dissolve run in parallel. On a test file with a boolean between two subdivided cubes (~6 million verts) this gives a 10% speed increase (49.5s to 45s) on my 6 core CPU. Also there is an optimization of other_tri_if_manifold to skip the contains-call and get the pointer directly. This reduces CPU time for find_patches from 5s to 2.2s on the same test file. --- source/blender/blenlib/intern/mesh_boolean.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index f74c17d6ee1..00d53a010b0 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -149,11 +149,9 @@ class TriMeshTopology : NonCopyable { * Else return NO_INDEX. */ int other_tri_if_manifold(Edge e, int t) const { - if (edge_tri_.contains(e)) { - auto *p = edge_tri_.lookup(e); - if (p->size() == 2) { - return ((*p)[0] == t) ? (*p)[1] : (*p)[0]; - } + auto p = edge_tri_.lookup_ptr(e); + if (p != nullptr && (*p)->size() == 2) { + return ((**p)[0] == t) ? (**p)[1] : (**p)[0]; } return NO_INDEX; } @@ -3336,9 +3334,13 @@ static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out, std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n"; } /* For now: need plane normals for all triangles. */ - for (Face *tri : tm_out.faces()) { - tri->populate_plane(false); - } + const int grainsize = 1024; + parallel_for(tm_out.face_index_range(), grainsize, [&](IndexRange range) { + for (int i : range) { + Face *tri = tm_out.face(i); + tri->populate_plane(false); + } + }); /* Gather all output triangles that are part of each input face. * face_output_tris[f] will be indices of triangles in tm_out * that have f as their original face. */ -- cgit v1.2.3 From 0ea0ccc4ffd573739aeb9e67e83dbfb3604887c7 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 2 Jun 2021 21:24:09 +0200 Subject: FFmpeg: Fix H264 lossless render not lossless While encoder parameters for lossless encoding are set correctly, output is not lossless due to pixel format being set to `AV_PIX_FMT_YUV420P` which is inherently lossy due to chroma subsampling. This was reported in T61569 and was merged to T57397, but there were 2 bugs - one for encoding and one for decoding. Set pixel format to `AV_PIX_FMT_YUV444P` when rendering lossless H264 files. This format isn't available in `codec->pix_fmts[0]` and it looks, that it has to be hard-coded. Reviewed By: sergey Differential Revision: D11458 --- source/blender/blenkernel/intern/writeffmpeg.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 60c216a8401..218552b0e74 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -729,6 +729,12 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } + /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */ + if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && + context->ffmpeg_crf == 0) { + c->pix_fmt = AV_PIX_FMT_YUV444P; + } + if (codec_id == AV_CODEC_ID_PNG) { if (rd->im_format.planes == R_IMF_PLANES_RGBA) { c->pix_fmt = AV_PIX_FMT_RGBA; -- cgit v1.2.3 From a9dfde7b49ebdcb8d775e5942fb5eb7522946dbe Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 2 Jun 2021 21:25:37 +0200 Subject: FFmpeg: Update proxy settings Changes in rBce649c73446e, affected established proxy codec preset. Presets were not working and all presets were similar to `veryfast`. Tunes are now working too, so `fastdecode` tune can be used. I have measured little improvement, but I tested this only on 2 machines and I have been informed that `fastdecode` tune does influence decoding performance for some users. Change preset from `slow` to `veryfast` and add tune `fastdecode` Reviewed By: sergey Differential Revision: https://developer.blender.org/D11454 --- source/blender/imbuf/intern/indexer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index e1e6cc677ed..930c0092a9b 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -535,8 +535,11 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( AVDictionary *codec_opts = NULL; /* High quality preset value. */ av_dict_set_int(&codec_opts, "crf", crf, 0); - /* Prefer smaller file-size. */ - av_dict_set(&codec_opts, "preset", "slow", 0); + /* Prefer smaller file-size. Presets from veryslow to veryfast produce output with very similar + * file-size, but there is big difference in performance. In some cases veryfast preset will + * produce smallest file-size. */ + av_dict_set(&codec_opts, "preset", "veryfast", 0); + av_dict_set(&codec_opts, "tune", "fastdecode", 0); if (rv->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { rv->c->thread_count = 0; -- cgit v1.2.3 From 1f557867916bd6ab43ced4b4fd651a7d1d11f8ac Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 2 Jun 2021 21:29:38 +0200 Subject: Fix T57397: Movies are blurred after sws_scale Images with 4:2:2 and 4:4:4 chroma subsampling were blurred when `SWS_FAST_BILINEAR` interpolation is set for `anim->img_convert_ctx`. Use `SWS_BILINEAR` interpolation for all movies, as performance is not impacted by this change. Reviewed By: sergey Differential Revision: https://developer.blender.org/D11457 --- source/blender/imbuf/intern/anim_movie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 5918a4bf16e..5058bd2f637 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -719,7 +719,7 @@ static int startffmpeg(struct anim *anim) anim->x, anim->y, AV_PIX_FMT_RGBA, - SWS_FAST_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT, + SWS_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); -- cgit v1.2.3 From 2ee575fc1f2b13f083bc5996e20e7350570be546 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 2 Jun 2021 21:36:09 +0200 Subject: Cleanup: Strip duplication code Remove unused flag `SEQ_DUPE_ANIM` and code used by this flag. Remove flag `SEQ_DUPE_CONTEXT` and refactor code, to split operator logic from duplication code. Reduce indentation level in for loop. Reviewed By: sergey Differential Revision: https://developer.blender.org/D11318 --- .../editors/space_sequencer/sequencer_edit.c | 20 ++++++++++----- source/blender/sequencer/SEQ_sequencer.h | 2 -- source/blender/sequencer/SEQ_utils.h | 1 + source/blender/sequencer/intern/sequencer.c | 29 ++++++---------------- 4 files changed, 23 insertions(+), 29 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 53ddc818cd1..d844b8c18c6 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1582,21 +1582,29 @@ static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, false); - ListBase nseqbase = {NULL, NULL}; - if (ed == NULL) { return OPERATOR_CANCELLED; } - SEQ_sequence_base_dupli_recursive(scene, scene, &nseqbase, ed->seqbasep, SEQ_DUPE_CONTEXT, 0); + Sequence *active_seq = SEQ_select_active_get(scene); + ListBase duplicated = {NULL, NULL}; + + SEQ_sequence_base_dupli_recursive(scene, scene, &duplicated, ed->seqbasep, 0, 0); + ED_sequencer_deselect_all(scene); - if (nseqbase.first) { - Sequence *seq = nseqbase.first; + if (duplicated.first) { + Sequence *seq = duplicated.first; /* Rely on the nseqbase list being added at the end. * Their UUIDs has been re-generated by the SEQ_sequence_base_dupli_recursive(), */ - BLI_movelisttolist(ed->seqbasep, &nseqbase); + BLI_movelisttolist(ed->seqbasep, &duplicated); + /* Handle duplicated strips: set active, select, ensure unique name and duplicate animation + * data. */ for (; seq; seq = seq->next) { + if (STREQ(active_seq != NULL && seq->name, active_seq->name)) { + SEQ_select_active_set(scene, seq); + } + seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK); SEQ_ensure_unique_name(seq, scene); } diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index 63df886d31f..ad0815892f7 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -46,8 +46,6 @@ enum { /* seq_dupli' flags */ #define SEQ_DUPE_UNIQUE_NAME (1 << 0) -#define SEQ_DUPE_CONTEXT (1 << 1) -#define SEQ_DUPE_ANIM (1 << 2) #define SEQ_DUPE_ALL (1 << 3) /* otherwise only selected are copied */ #define SEQ_DUPE_IS_RECURSIVE_CALL (1 << 4) diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h index 9d529089ffc..a4dc80d75db 100644 --- a/source/blender/sequencer/SEQ_utils.h +++ b/source/blender/sequencer/SEQ_utils.h @@ -61,6 +61,7 @@ int SEQ_recursive_apply(struct Sequence *seq, int (*apply_fn)(struct Sequence *, void *), void *arg); void SEQ_ensure_unique_name(struct Sequence *seq, struct Scene *scene); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index 4acb6a206be..d0bc41062a1 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -516,10 +516,6 @@ static Sequence *seq_dupli(const Scene *scene_src, if (dupe_flag & SEQ_DUPE_UNIQUE_NAME) { SEQ_sequence_base_unique_name_recursive(&scene_dst->ed->seqbase, seqn); } - - if (dupe_flag & SEQ_DUPE_ANIM) { - SEQ_dupe_animdata(scene_dst, seq->name + 2, seqn->name + 2); - } } return seqn; @@ -565,30 +561,21 @@ void SEQ_sequence_base_dupli_recursive(const Scene *scene_src, { Sequence *seq; Sequence *seqn = NULL; - Sequence *last_seq = SEQ_select_active_get((Scene *)scene_src); - /* always include meta's strips */ - int dupe_flag_recursive = dupe_flag | SEQ_DUPE_ALL | SEQ_DUPE_IS_RECURSIVE_CALL; for (seq = seqbase->first; seq; seq = seq->next) { seq->tmp = NULL; if ((seq->flag & SELECT) || (dupe_flag & SEQ_DUPE_ALL)) { seqn = seq_dupli(scene_src, scene_dst, nseqbase, seq, dupe_flag, flag); - if (seqn) { /*should never fail */ - if (dupe_flag & SEQ_DUPE_CONTEXT) { - seq->flag &= ~SEQ_ALLSEL; - seqn->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK); - } - if (seq->type == SEQ_TYPE_META) { - SEQ_sequence_base_dupli_recursive( - scene_src, scene_dst, &seqn->seqbase, &seq->seqbase, dupe_flag_recursive, flag); - } + if (seqn == NULL) { + continue; /* Should never fail. */ + } - if (dupe_flag & SEQ_DUPE_CONTEXT) { - if (seq == last_seq) { - SEQ_select_active_set(scene_dst, seqn); - } - } + if (seq->type == SEQ_TYPE_META) { + /* Always include meta all strip children. */ + int dupe_flag_recursive = dupe_flag | SEQ_DUPE_ALL | SEQ_DUPE_IS_RECURSIVE_CALL; + SEQ_sequence_base_dupli_recursive( + scene_src, scene_dst, &seqn->seqbase, &seq->seqbase, dupe_flag_recursive, flag); } } } -- cgit v1.2.3 From 925df8ef26a353497c8088657a82b6b8545c67c1 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 2 Jun 2021 21:41:38 +0200 Subject: VSE: Add strip-time intersection test function Use SEQ_time_strip_intersects_frame function to test if strip intersects with frame. Note: There are cases where this function should not be used. For example splitting strips require at least 1 frame "inside" strip. Another example is drawing, where playhead technically doesn't intersect strip, but it is rendered, because current frame has "duration" or "thickness" of 1 frame. Reviewed By: sergey Differential Revision: https://developer.blender.org/D11320 --- .../blender/editors/space_sequencer/sequencer_select.c | 3 ++- source/blender/sequencer/SEQ_time.h | 1 + source/blender/sequencer/intern/render.c | 3 ++- source/blender/sequencer/intern/strip_relations.c | 4 ++-- source/blender/sequencer/intern/strip_time.c | 16 +++++++++++++++- source/blender/sequencer/intern/utils.c | 3 ++- 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 867b8e3d40a..7e515271b13 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -42,6 +42,7 @@ #include "SEQ_iterator.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" +#include "SEQ_time.h" #include "SEQ_transform.h" /* For menu, popup, icons, etc. */ @@ -1187,7 +1188,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) test = (timeline_frame <= seq->startdisp); break; case 2: - test = (timeline_frame <= seq->enddisp) && (timeline_frame >= seq->startdisp); + test = SEQ_time_strip_intersects_frame(seq, timeline_frame); break; } diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index 31549ff3994..2a875370830 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -45,6 +45,7 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene, void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq); void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq); int SEQ_time_cmp_time_startdisp(const void *a, const void *b); +bool SEQ_time_strip_intersects_frame(struct Sequence *seq, const int timeline_frame); #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 8ed769880a4..4b8df111f07 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -70,6 +70,7 @@ #include "SEQ_proxy.h" #include "SEQ_render.h" #include "SEQ_sequencer.h" +#include "SEQ_time.h" #include "SEQ_utils.h" #include "effects.h" @@ -309,7 +310,7 @@ static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timelin SeqCollection *collection = SEQ_collection_create(); LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) { + if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { SEQ_collection_append_strip(seq, collection); } } diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c index 1215cb78b56..7c5a3f031db 100644 --- a/source/blender/sequencer/intern/strip_relations.c +++ b/source/blender/sequencer/intern/strip_relations.c @@ -259,7 +259,7 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render) SEQ_prefetch_stop(scene); for (seq = seqbase->first; seq; seq = seq->next) { - if (for_render && CFRA >= seq->startdisp && CFRA <= seq->enddisp) { + if (for_render && SEQ_time_strip_intersects_frame(seq, CFRA)) { continue; } @@ -358,7 +358,7 @@ void SEQ_relations_update_changed_seq_and_deps(Scene *scene, static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int timeline_frame) { for (Sequence *seq = seqbase->first; seq != NULL; seq = seq->next) { - if (seq->enddisp < timeline_frame || seq->startdisp > timeline_frame) { + if (!SEQ_time_strip_intersects_frame(seq, timeline_frame)) { SEQ_relations_sequence_free_anim(seq); } if (seq->type == SEQ_TYPE_META) { diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 40d7fade308..e64550f1428 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -409,7 +409,7 @@ static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_ { Sequence *seq; SEQ_ITERATOR_FOREACH (seq, all_strips) { - if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) { + if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { return true; } } @@ -468,3 +468,17 @@ void seq_time_gap_info_get(const Scene *scene, } } } + +/** + * Test if strip intersects with timeline frame. + * Note: This checks if strip would be rendered at this frame. For rendering it is assumed, that + * timeline frame has width of 1 frame and therefore ends at timeline_frame + 1 + * + * \param seq: Sequence to be checked + * \param timeline_frame: absolute frame position + * \return true if strip intersects with timeline frame. + */ +bool SEQ_time_strip_intersects_frame(Sequence *seq, const int timeline_frame) +{ + return (seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame); +} diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index cf1d7d66476..9aeb2961751 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -43,6 +43,7 @@ #include "SEQ_relations.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" +#include "SEQ_time.h" #include "SEQ_utils.h" #include "IMB_imbuf.h" @@ -406,7 +407,7 @@ const Sequence *SEQ_get_topmost_sequence(const Scene *scene, int frame) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->flag & SEQ_MUTE || seq->startdisp > frame || seq->enddisp <= frame) { + if (seq->flag & SEQ_MUTE || !SEQ_time_strip_intersects_frame(seq, frame)) { continue; } /* Only use strips that generate an image, not ones that combine -- cgit v1.2.3 From 5af72258161a6e903a5c526f2796db2a4e5bc483 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 2 Jun 2021 21:52:36 +0200 Subject: Cleanup: Fix build warnings --- source/blender/editors/space_sequencer/sequencer_edit.c | 2 +- source/blender/sequencer/SEQ_time.h | 2 +- source/blender/sequencer/intern/strip_time.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index d844b8c18c6..f22a515a8f2 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1601,7 +1601,7 @@ static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) /* Handle duplicated strips: set active, select, ensure unique name and duplicate animation * data. */ for (; seq; seq = seq->next) { - if (STREQ(active_seq != NULL && seq->name, active_seq->name)) { + if (active_seq != NULL && STREQ(seq->name, active_seq->name)) { SEQ_select_active_set(scene, seq); } seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK); diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index 2a875370830..67d3a2e5960 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -45,7 +45,7 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene, void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq); void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq); int SEQ_time_cmp_time_startdisp(const void *a, const void *b); -bool SEQ_time_strip_intersects_frame(struct Sequence *seq, const int timeline_frame); +bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, const int timeline_frame); #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index e64550f1428..b8b6f62c7aa 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -478,7 +478,7 @@ void seq_time_gap_info_get(const Scene *scene, * \param timeline_frame: absolute frame position * \return true if strip intersects with timeline frame. */ -bool SEQ_time_strip_intersects_frame(Sequence *seq, const int timeline_frame) +bool SEQ_time_strip_intersects_frame(const Sequence *seq, const int timeline_frame) { return (seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame); } -- cgit v1.2.3 From 4d64de2853c81239d55c6bfc93eb2c60e235e8d1 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 2 Jun 2021 17:55:25 -0300 Subject: Cleanup: Remove unused 'ExtractTaskData's members --- source/blender/draw/intern/draw_cache_extract_mesh.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 622ea191a0a..180ab50257f 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -369,9 +369,6 @@ BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr, /** \name ExtractTaskData * \{ */ struct ExtractTaskData { - void *next = nullptr; - void *prev = nullptr; - const MeshRenderData *mr = nullptr; MeshBatchCache *cache = nullptr; /* #UserData is shared between the iterations as it holds counters to detect if the -- cgit v1.2.3 From 2dcb6782e06d285c7d500c34c85c0ab86c7c183a Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 2 Jun 2021 18:04:48 -0300 Subject: Draw Mesh Extractor: Fix used thread count Some threads were always idle because of this. --- source/blender/draw/intern/draw_cache_extract_mesh.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 180ab50257f..b48ee1ddc3f 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -866,7 +866,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool use_thread = (mr->loop_len + mr->loop_loose_len) > CHUNK_SIZE; if (use_thread) { - uint threads_to_use = 0; + uint single_threaded_extractors_len = 0; /* First run the requested extractors that do not support asynchronous ranges. */ for (const ExtractorRunData &run_data : extractors) { @@ -879,8 +879,8 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata); BLI_task_graph_edge_create(task_node_mesh_render_data, task_node); + single_threaded_extractors_len++; } - threads_to_use++; } /* Distribute the remaining extractors into ranges per core. */ @@ -893,9 +893,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, * fill the rest of the threads for range operations. */ int num_threads = BLI_task_scheduler_num_threads(); - if (threads_to_use < num_threads) { - num_threads -= threads_to_use; - } + num_threads -= single_threaded_extractors_len % num_threads; UserDataInitTaskData *user_data_init_task_data = new UserDataInitTaskData(); struct TaskNode *task_node_user_data_init = user_data_init_task_node_create( -- cgit v1.2.3 From b60a72eaab3ac25dbcbcaeedb87719624b4d8739 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 3 Jun 2021 02:38:59 +1000 Subject: Fix invalid return values from file_execute Error in 6c8c30d865ee8aafc3a088ce97b1caa4c4cc9ed7 --- source/blender/editors/space_file/file_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 36f8476d0c9..7c14f4659eb 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1744,7 +1744,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile) /* directory change */ if (file && (file->typeflag & FILE_TYPE_DIR)) { if (!file->relpath) { - return OPERATOR_CANCELLED; + return false; } if (FILENAME_IS_PARENT(file->relpath)) { @@ -1783,7 +1783,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile) WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC); } - return OPERATOR_FINISHED; + return true; } static int file_exec(bContext *C, wmOperator *UNUSED(op)) -- cgit v1.2.3 From 2a868d277e323df32195d42e52d49d80502a6bde Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 3 Jun 2021 10:35:46 +1000 Subject: Cleanup: use doxy sections for node_relationships.cc --- .../editors/space_node/node_relationships.cc | 98 ++++++++++++++++++---- 1 file changed, 84 insertions(+), 14 deletions(-) diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 212724dc39a..9a931176e42 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -55,7 +55,9 @@ #include "node_intern.h" /* own include */ -/* ****************** Relations helpers *********************** */ +/* -------------------------------------------------------------------- */ +/** \name Relations Helpers + * \{ */ static bool ntree_has_drivers(bNodeTree *ntree) { @@ -162,7 +164,11 @@ bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node) return false; } -/* ****************** Add *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Node + * \{ */ struct bNodeListItem { struct bNodeListItem *next, *prev; @@ -590,7 +596,11 @@ static void snode_autoconnect(Main *bmain, MEM_freeN(nodelist); } -/* *************************** link viewer op ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Link Viewer Operator + * \{ */ static int node_link_viewer(const bContext *C, bNode *tonode) { @@ -744,7 +754,11 @@ void NODE_OT_link_viewer(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* *************************** add link op ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Link Operator + * \{ */ static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag)) { @@ -1181,7 +1195,11 @@ void NODE_OT_link(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } -/* ********************** Make Link operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Make Link Operator + * \{ */ /* makes a link between selected output and input sockets */ static int node_make_link_exec(bContext *C, wmOperator *op) @@ -1224,7 +1242,12 @@ void NODE_OT_link_make(wmOperatorType *ot) ot->srna, "replace", false, "Replace", "Replace socket connections with the new links"); } -/* ********************** Node Link Intersect ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Link Intersect + * \{ */ + static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int tot) { float coord_array[NODE_LINK_RESOL + 1][2]; @@ -1241,7 +1264,12 @@ static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int return false; } -/* ********************** Cut Link operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cut Link Operator + * \{ */ + static int cut_links_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -1333,7 +1361,11 @@ void NODE_OT_links_cut(wmOperatorType *ot) RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX); } -/* ********************** Mute links operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mute Links Operator + * \{ */ static int mute_links_exec(bContext *C, wmOperator *op) { @@ -1435,7 +1467,11 @@ void NODE_OT_links_mute(wmOperatorType *ot) RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX); } -/* ********************** Detach links operator ***************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Detach Links Operator + * \{ */ static int detach_links_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1472,7 +1508,11 @@ void NODE_OT_links_detach(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Set Parent ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set Parent Operator + * \{ */ static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1514,7 +1554,11 @@ void NODE_OT_parent_set(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Join Nodes ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Join Nodes Operator + * \{ */ /* tags for depth-first search */ #define NODE_JOIN_DONE 1 @@ -1607,7 +1651,11 @@ void NODE_OT_join(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Attach ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Attach Operator + * \{ */ static bNode *node_find_frame_to_attach(ARegion *region, const bNodeTree *ntree, @@ -1690,7 +1738,11 @@ void NODE_OT_attach(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Detach ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Detach Operator + * \{ */ /* tags for depth-first search */ #define NODE_DETACH_DONE 1 @@ -1761,7 +1813,11 @@ void NODE_OT_detach(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************* automatic node insert on dragging ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Automatic Node Insert on Dragging + * \{ */ /* prevent duplicate testing code below */ static bool ed_node_link_conditions(ScrArea *area, @@ -1879,6 +1935,12 @@ void ED_node_link_intersect_test(ScrArea *area, int test) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Insert Offset Operator + * \{ */ + static int get_main_socket_priority(const bNodeSocket *socket) { switch ((eNodeSocketDatatype)socket->type) { @@ -2263,6 +2325,12 @@ void NODE_OT_insert_offset(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Note Link Insert + * \{ */ + /* assumes link with NODE_LINKFLAG_HILITE set */ void ED_node_link_insert(Main *bmain, ScrArea *area) { @@ -2319,3 +2387,5 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) } } } + +/** \} */ -- cgit v1.2.3 From 17f72be3cb7c50044a10c00e266253f5896f0770 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 3 Jun 2021 10:46:16 +1000 Subject: Cleanup: spelling in comments, correct outdated comments --- source/blender/editors/space_node/drawnode.cc | 8 +++++++- source/blender/editors/space_node/node_edit.cc | 2 +- source/blender/editors/space_node/node_group.cc | 2 +- source/blender/editors/space_node/node_relationships.cc | 2 +- source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c | 2 +- source/blender/imbuf/intern/indexer.c | 10 +++++----- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 982af78dfde..c6fa6c78f05 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3294,7 +3294,11 @@ static void node_texture_set_butfunc(bNodeType *ntype) } } -/* ****** init draw callbacks for all tree types, only called in usiblender.c, once ************ */ +/* -------------------------------------------------------------------- */ +/** \name Init Draw Callbacks For All Tree Types + * + * Only called on node initialization, once. + * \{ */ static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { @@ -3366,6 +3370,8 @@ static void node_socket_undefined_interface_draw_color(bContext *UNUSED(C), r_color[3] = 1.0f; } +/** \} */ + void ED_node_init_butfuncs(void) { /* Fallback types for undefined tree, nodes, sockets diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index b98a3fcd59b..90df7e80c38 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1472,7 +1472,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op)) RNA_string_set(&op_ptr, "layer", view_layer->name); RNA_string_set(&op_ptr, "scene", sce->id.name + 2); - /* to keep keypositions */ + /* To keep keyframe positions. */ sce->r.scemode |= R_NO_FRAME_UPDATE; WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr); diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index c4880e1b49a..34450016698 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -219,7 +219,7 @@ static void animation_basepath_change_free(AnimationBasePathChange *basepath_cha /* returns 1 if its OK */ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode) { - /* clear new pointers, set in copytree */ + /* Clear new pointers, set in #ntreeCopyTree_ex_new_pointers. */ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->new_node = nullptr; } diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 9a931176e42..57dc0b6fef2 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -2042,7 +2042,7 @@ static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, con #define NODE_INSOFS_ANIM_DURATION 0.25f /** - * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, similar + * Callback that applies #NodeInsertOfsData.offset_x to a node or its parent, similar * to node_link_insert_offset_output_chain_cb below, but with slightly different logic */ static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index b546aaa683e..a9d17f19206 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -2064,7 +2064,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), dot_r = dot_v3v3_db(Rv, tri->gn); dot_f = dot_v3v3_db(Cv, tri->gn); - /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possibile that _some_ + /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_ * faces in perspective mode would get erroneously caught in this condition where they really are * legit faces that would produce occlusion, but haven't encountered those yet in my test files. */ diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 930c0092a9b..46b28086129 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -526,8 +526,8 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->c->time_base.num = 1; rv->st->time_base = rv->c->time_base; - /* This range matches eFFMpegCrf. Crf_range_min corresponds to lowest quality, crf_range_max to - * highest quality. */ + /* This range matches #eFFMpegCrf. `crf_range_min` corresponds to lowest quality, + * `crf_range_max` to highest quality. */ const int crf_range_min = 32; const int crf_range_max = 17; int crf = round_fl_to_int((quality / 100.0f) * (crf_range_max - crf_range_min) + crf_range_min); @@ -535,9 +535,9 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( AVDictionary *codec_opts = NULL; /* High quality preset value. */ av_dict_set_int(&codec_opts, "crf", crf, 0); - /* Prefer smaller file-size. Presets from veryslow to veryfast produce output with very similar - * file-size, but there is big difference in performance. In some cases veryfast preset will - * produce smallest file-size. */ + /* Prefer smaller file-size. Presets from `veryslow` to `veryfast` produce output with very + * similar file-size, but there is big difference in performance. + * In some cases `veryfast` preset will produce smallest file-size. */ av_dict_set(&codec_opts, "preset", "veryfast", 0); av_dict_set(&codec_opts, "tune", "fastdecode", 0); -- cgit v1.2.3 From a51f8f94d5dc7c0b8f61de35f277052144299662 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 3 Jun 2021 11:27:18 +1000 Subject: Cleanup: use ascii characters instead of unicode where possible Follow own code style docs. --- source/blender/blenkernel/intern/fcurve.c | 3 +-- source/blender/bmesh/intern/bmesh_mesh.c | 2 +- source/blender/draw/engines/eevee/eevee_lights.c | 6 +++--- source/blender/makesrna/intern/rna_ui.c | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 30d5a54b479..68ed3c239ef 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1559,8 +1559,7 @@ void BKE_fcurve_correct_bezpart(const float v1[2], float v2[2], float v3[2], con } /** - . - * Find roots of cubic equation (c0 x³ + c1 x² + c2 x + c3) + * Find roots of cubic equation (c0 x^3 + c1 x^2 + c2 x + c3) * \return number of roots in `o`. * * \note it is up to the caller to allocate enough memory for `o`. diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index 5e879d41d43..69bb61a4f7d 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -1724,7 +1724,7 @@ static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops, const bool do if (use_sel_face_history) { /* Using face history allows to select a single loop from a single face... - * Note that this is On² piece of code, + * Note that this is O(n^2) piece of code, * but it is not designed to be used with huge selection sets, * rather with only a few items selected at most.*/ /* Goes from last selected to the first selected element. */ diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index cba86d058ea..e23a5a81169 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -96,7 +96,7 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli) } } else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) { - power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* 1/(4*r²*Pi²) */ + power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* `1/(4*(r^2)*(Pi^2))` */ /* for point lights (a.k.a radius == 0.0) */ // power = M_PI * M_PI * 0.78; /* XXX : Empirical, Fit cycles power */ @@ -106,7 +106,7 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli) /* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that we * cannot reproduce so we account for that by scaling the light power. This function is the * result of a rough manual fitting. */ - power += 1.0f / (2.0f * M_PI); /* power *= 1 + r²/2 */ + power += 1.0f / (2.0f * M_PI); /* `power *= 1 + (r^2)/2` */ } return power; } @@ -257,7 +257,7 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) float power = max_fff(UNPACK3(evli->color)) * evli->volume; if (power > 0.0f && evli->light_type != LA_SUN) { /* The limit of the power attenuation function when the distance to the light goes to 0 is - * 2 / r² where r is the light radius. We need to find the right radius that emits at most + * `2 / r^2` where r is the light radius. We need to find the right radius that emits at most * the volume light upper bound. Inverting the function we get: */ float min_radius = 1.0f / sqrtf(0.5f * upper_bound / power); /* Square it here to avoid a multiplication inside the shader. */ diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index aefb77c4077..8f596ec6a5c 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -599,7 +599,7 @@ static void uilist_filter_items(uiList *ui_list, items_shown = flt_data->items_shown = shown_idx; flt_data->items_filter_neworder = MEM_mallocN(sizeof(int) * items_shown, __func__); /* And now, bring back new indices into the [0, items_shown[ range! - * XXX This is O(N²)... :/ + * XXX This is O(N^2). :/ */ for (shown_idx = 0, prev_ni = -1; shown_idx < items_shown; shown_idx++) { for (i = 0, t_ni = len, t_idx = -1; i < items_shown; i++) { -- cgit v1.2.3 From 2ef192a55b2ca65f07f678aada199357b06713e9 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 3 Jun 2021 10:18:44 +0200 Subject: IDManagement: Collection: Fix several issues in relationships building code. `BKE_main_collections_parent_relations_rebuild`, `BKE_collection_parent_relations_rebuild` anf their internal dependencies had two issues fixed by this commit: * Main one was that a same collection could be processed several times, sometimes even in an infinite loop (in some rare corner cases), by `collection_parents_rebuild_recursive`. * More exotic, code here would not ensure that the collections it was processing were actually in Main (or a master one from a scene in Main), which became an issue with some advanced ID management processes involving partially out-of-main remapping, like liboverride resync. --- source/blender/blenkernel/intern/collection.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index be827cd338d..5a7fe71de8e 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1643,6 +1643,13 @@ void BKE_collection_parent_relations_rebuild(Collection *collection) continue; } + /* Can happen when remaping data partially out-of-Main (during advanced ID management + * operations like liboverride resync e.g.). */ + if ((child->collection->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)) != 0) { + continue; + } + + BLI_assert(collection_find_parent(child->collection, collection) == NULL); CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), __func__); cparent->collection = collection; BLI_addtail(&child->collection->parents, cparent); @@ -1651,10 +1658,19 @@ void BKE_collection_parent_relations_rebuild(Collection *collection) static void collection_parents_rebuild_recursive(Collection *collection) { + /* A same collection may be child of several others, no need to process it more than once. */ + if ((collection->tag & COLLECTION_TAG_RELATION_REBUILD) == 0) { + return; + } + BKE_collection_parent_relations_rebuild(collection); collection->tag &= ~COLLECTION_TAG_RELATION_REBUILD; for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) { + /* See comment above in `BKE_collection_parent_relations_rebuild`. */ + if ((collection->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)) != 0) { + continue; + } collection_parents_rebuild_recursive(child->collection); } } @@ -1678,6 +1694,8 @@ void BKE_main_collections_parent_relations_rebuild(Main *bmain) /* This function can be called from readfile.c, when this pointer is not guaranteed to be NULL. */ if (scene->master_collection != NULL) { + BLI_assert(BLI_listbase_is_empty(&scene->master_collection->parents)); + scene->master_collection->tag |= COLLECTION_TAG_RELATION_REBUILD; collection_parents_rebuild_recursive(scene->master_collection); } } -- cgit v1.2.3 From 2ae4e860f67c8a09976dcb18f101ea4e8c215079 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 3 Jun 2021 10:24:38 +0200 Subject: LibOverride: ensure proper indirect tag for 'virtual' linked IDs. Ensure 'virtual' linked override IDs generated by the recursive resync process are tagged as indirectly linked data. This is needed to avoid the 'missing data' messages on those virtual data-blocks after saving and reloading. --- source/blender/blenkernel/intern/lib_override.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 9a2887a1d16..6718c6ab79f 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1074,6 +1074,10 @@ bool BKE_lib_override_library_resync(Main *bmain, BLI_assert(/*id_override_new->lib == NULL || */ id_override_new->lib == id->lib); BLI_assert(id_override_old == NULL || id_override_old->lib == id_root->lib); id_override_new->lib = id_root->lib; + /* Remap step below will tag directly linked ones properly as needed. */ + if (ID_IS_LINKED(id_override_new)) { + id_override_new->tag |= LIB_TAG_INDIRECT; + } if (id_override_old != NULL) { /* Swap the names between old override ID and new one. */ -- cgit v1.2.3 From 7b8d8122774210e740ba9aeed83cb520447483d1 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 3 Jun 2021 13:33:09 +0200 Subject: Cleanup: make format --- source/blender/blenkernel/intern/writeffmpeg.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 218552b0e74..92d19f18a93 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -730,8 +730,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */ - if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && - context->ffmpeg_crf == 0) { + if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && context->ffmpeg_crf == 0) { c->pix_fmt = AV_PIX_FMT_YUV444P; } -- cgit v1.2.3 From 92f8a6ac21acee5ee1d5151ddf11570afcaa64a8 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 3 Jun 2021 13:12:51 +0200 Subject: Fix T88762: UI using tab to enter next button could clamp the hard min/ max unneccessarily Since rB298d5eb66916 [which was needed to update buttons with custom property range functions correctly], using tab would always clamp (hardmin/hardmax) properties which were using FLT_MAX / INT_MAX as range in their property definitions. The clamping of rB298d5eb66916 was copied over from rB9b7f44ceb56c [where it was used for the softmin/softmax], and while the re-evaluation of hardmin/hardmax is needed for custom property range functions, the clamping should actually not take place. There are many properties using FLT_MAX / INT_MAX etc. and while it probably would be good to update these with ranges that make more sense -- not using FLT_MAX / INT_MAX would not have done the clamping here -- there should not be an arbitrary limit to these and they should stay as they are. Maniphest Tasks: T88762 Differential Revision: https://developer.blender.org/D11473 --- source/blender/editors/interface/interface.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index a31efefd99c..f6b2a6a1bc6 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -3207,19 +3207,17 @@ void ui_but_range_set_hard(uiBut *but) const PropertyType type = RNA_property_type(but->rnaprop); - /* clamp button range to something reasonable in case - * we get -inf/inf from RNA properties */ if (type == PROP_INT) { int imin, imax; RNA_property_int_range(&but->rnapoin, but->rnaprop, &imin, &imax); - but->hardmin = (imin == INT_MIN) ? -1e4 : imin; - but->hardmax = (imin == INT_MAX) ? 1e4 : imax; + but->hardmin = imin; + but->hardmax = imax; } else if (type == PROP_FLOAT) { float fmin, fmax; RNA_property_float_range(&but->rnapoin, but->rnaprop, &fmin, &fmax); - but->hardmin = (fmin == -FLT_MAX) ? (float)-1e4 : fmin; - but->hardmax = (fmax == FLT_MAX) ? (float)1e4 : fmax; + but->hardmin = fmin; + but->hardmax = fmax; } } -- cgit v1.2.3 From e011e4ce76cab3e4cb3f0024f7dd0108220ffe4e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 3 Jun 2021 15:00:10 +0200 Subject: LibOverride: Add `override_hierarchy_create`to ID's RNA API. --- source/blender/blenkernel/BKE_lib_override.h | 3 +- source/blender/blenkernel/intern/lib_override.c | 21 +++++++++--- source/blender/editors/object/object_relations.c | 2 +- .../editors/space_outliner/outliner_tools.c | 2 +- source/blender/makesrna/intern/rna_ID.c | 39 ++++++++++++++++++++++ 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 0275c2c235c..4dc99e64cf2 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -76,7 +76,8 @@ bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct ID *id_root, - struct ID *id_reference); + struct ID *id_reference, + struct ID **r_id_root_override); bool BKE_lib_override_library_template_create(struct ID *id); bool BKE_lib_override_library_proxy_convert(struct Main *bmain, struct Scene *scene, diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 6718c6ab79f..b76e1b10ed5 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -838,7 +838,7 @@ static void lib_override_library_create_post_process(Main *bmain, /** * Advanced 'smart' function to create fully functional overrides. * - * \note Currently it only does special things if given \a id_root is an object of collection, more + * \note Currently it only does special things if given \a id_root is an object or collection, more * specific behaviors may be added in the future for other ID types. * * \note It will override all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at @@ -848,17 +848,30 @@ 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. * \return true if override was successfully created. */ -bool BKE_lib_override_library_create( - Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference) +bool BKE_lib_override_library_create(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + ID *id_root, + ID *id_reference, + ID **r_id_root_override) { + if (r_id_root_override != NULL) { + *r_id_root_override = NULL; + } + const bool success = lib_override_library_create_do(bmain, id_root); if (!success) { return success; } + if (r_id_root_override != NULL) { + *r_id_root_override = id_root->newid; + } + lib_override_library_create_post_process( bmain, scene, view_layer, id_root, id_reference, NULL, false); @@ -928,7 +941,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE); - return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference); + return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL); } /** diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 127d8681d3c..2c188e310db 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2454,7 +2454,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); const bool success = BKE_lib_override_library_create( - bmain, scene, view_layer, id_root, &obact->id); + bmain, scene, view_layer, id_root, &obact->id, NULL); /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index da47fd29549..f809bb13b42 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -846,7 +846,7 @@ static void id_override_library_create_fn(bContext *C, te->store_elem->id->tag |= LIB_TAG_DOIT; } success = BKE_lib_override_library_create( - bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference); + bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference, NULL); } else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) { success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != NULL; diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 2818f251085..17c79a2f5dc 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -611,6 +611,21 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) return local_id; } +static ID *rna_ID_override_hierarchy_create( + ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_reference) +{ + if (!ID_IS_OVERRIDABLE_LIBRARY(id)) { + return NULL; + } + + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + + ID *id_root_override = NULL; + BKE_lib_override_library_create(bmain, scene, view_layer, id, id_reference, &id_root_override); + + return id_root_override; +} + static void rna_ID_override_template_create(ID *id, ReportList *reports) { if (!U.experimental.use_override_templates) { @@ -1760,6 +1775,30 @@ static void rna_def_ID(BlenderRNA *brna) "Whether local usages of the linked ID should be remapped to the new " "library override of it"); + func = RNA_def_function(srna, "override_hierarchy_create", "rna_ID_override_hierarchy_create"); + RNA_def_function_ui_description( + func, + "Create an overridden local copy of this linked data-block, and most of its dependencies " + "when it is a Collection or and Object"); + RNA_def_function_flag(func, FUNC_USE_MAIN); + parm = RNA_def_pointer(func, "id", "ID", "", "New overridden local copy of the root ID"); + RNA_def_function_return(func, parm); + parm = RNA_def_pointer( + func, "scene", "Scene", "", "In which scene the new overrides should be instantiated"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "view_layer", + "ViewLayer", + "", + "In which view layer the new overrides should be instantiated"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + RNA_def_pointer(func, + "reference", + "ID", + "", + "Another ID (usually an Object or Collection) used to decide where to " + "instantiate the new overrides"); + func = RNA_def_function(srna, "override_template_create", "rna_ID_override_template_create"); RNA_def_function_ui_description(func, "Create an override template for this ID"); RNA_def_function_flag(func, FUNC_USE_REPORTS); -- cgit v1.2.3 From 797f6e1483a12fe4797e7cf9d435c042ddda40c4 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 3 Jun 2021 15:44:23 +0200 Subject: Fix missing updates in RNA override create functions. --- source/blender/makesrna/intern/rna_ID.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 17c79a2f5dc..43b65b087bf 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -608,6 +608,9 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) if (remap_local_usages) { BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); } + + WM_main_add_notifier(NC_ID | NA_ADDED, NULL); + return local_id; } @@ -623,6 +626,8 @@ static ID *rna_ID_override_hierarchy_create( ID *id_root_override = NULL; BKE_lib_override_library_create(bmain, scene, view_layer, id, id_reference, &id_root_override); + WM_main_add_notifier(NC_ID | NA_ADDED, NULL); + return id_root_override; } -- cgit v1.2.3 From 826bed4349fa2adb8d0fccbb192a3e550030a28d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 3 Jun 2021 16:12:26 +0200 Subject: LibOverride: Fix some fail cases with auto-resync. In some cases e.g. only objects would actually need resync, so collections on the override character would not be resynced, and if some objects were sharing relationships with others those could be lost/destroyed. --- source/blender/blenkernel/intern/lib_override.c | 90 ++++++++++++++++++------- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index b76e1b10ed5..80c544f8e5c 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1316,6 +1316,66 @@ bool BKE_lib_override_library_resync(Main *bmain, return success; } +/* Also tag ancestors overrides for resync. + * + * WARNING: Expects `bmain` to have valid relation data. + * + * NOTE: Related to `lib_override_library_main_resync_find_root_recurse` below. + * + * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to + * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and + * resync the whole hierarchy. + */ +static void lib_override_resync_tagging_finalize_recurse(Main *bmain, + ID *id, + const int library_indirect_level) +{ + if (id->lib != NULL && id->lib->temp_index > library_indirect_level) { + CLOG_ERROR( + &LOG, + "While processing indirect level %d, ID %s from lib %s of indirect level %d detected " + "as needing resync.", + library_indirect_level, + id->name, + id->lib->filepath, + id->lib->temp_index); + } + + MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); + BLI_assert(entry != NULL); + + if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { + /* This ID has already been processed. */ + return; + } + /* This way we won't process again that ID, should we encounter it again through another + * relationship hierarchy. */ + entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED; + + for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != NULL; + entry_item = entry_item->next) { + if (entry_item->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) { + continue; + } + ID *id_from = entry_item->id_pointer.from; + + /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */ + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib) { + id_from->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + CLOG_INFO(&LOG, + 3, + "ID %s (%p) now tagged as needing resync because they use %s (%p) that needs to " + "be overridden", + id_from->name, + id_from->lib, + id->name, + id->lib); + lib_override_resync_tagging_finalize_recurse(bmain, id_from, library_indirect_level); + } + break; + } +} + /* Ensures parent collection (or objects) in the same override group are also tagged for resync. * * This is needed since otherwise, some (new) ID added in one sub-collection might be used in @@ -1323,9 +1383,8 @@ bool BKE_lib_override_library_resync(Main *bmain, * sub-collections would be unaware that this is the same ID, and would re-generate several * overrides for it. * - * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to - * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and - * resync the whole hierarchy. */ + * NOTE: Related to `lib_override_resync_tagging_finalize` above. + */ static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level) { (*level)++; @@ -1414,6 +1473,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( /* Now check existing overrides, those needing resync will be the one either already tagged as * such, or the one using linked data that is now tagged as needing override. */ + BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); FOREACH_MAIN_ID_BEGIN (bmain, id) { if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { continue; @@ -1421,16 +1481,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) { CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib); - if (id->lib != NULL && id->lib->temp_index > library_indirect_level) { - CLOG_ERROR( - &LOG, - "While processing indirect level %d, ID %s from lib %s of indirect level %d detected " - "as needing resync.", - library_indirect_level, - id->name, - id->lib ? id->lib->filepath : "", - id->lib ? id->lib->temp_index : 0); - } + lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level); continue; } @@ -1455,18 +1506,9 @@ static void lib_override_library_main_resync_on_library_indirect_level( id->lib, id_to->name, id_to->lib); - if (id->lib != NULL && id->lib->temp_index > library_indirect_level) { - CLOG_ERROR(&LOG, - "While processing indirect level %d, ID %s from lib %s of indirect level %d " - "detected " - "as needing resync.", - library_indirect_level, - id->name, - id->lib ? id->lib->filepath : "", - id->lib ? id->lib->temp_index : 0); - } - break; + lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level); } + break; } } FOREACH_MAIN_ID_END; -- cgit v1.2.3 From ed6fd01ba9cbf4bd7f121ce7be5406c533fd49c2 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 3 Jun 2021 16:44:20 +0200 Subject: LibOverride: fix previous commit (rB826bed4349fa). --- source/blender/blenkernel/intern/lib_override.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 80c544f8e5c..beae8b59db4 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1354,16 +1354,17 @@ static void lib_override_resync_tagging_finalize_recurse(Main *bmain, for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != NULL; entry_item = entry_item->next) { - if (entry_item->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) { + if (entry_item->usage_flag & + (IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE | IDWALK_CB_LOOPBACK)) { continue; } ID *id_from = entry_item->id_pointer.from; /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */ - if (ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib) { + if (id_from != id && ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib) { id_from->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; CLOG_INFO(&LOG, - 3, + 4, "ID %s (%p) now tagged as needing resync because they use %s (%p) that needs to " "be overridden", id_from->name, @@ -1372,7 +1373,6 @@ static void lib_override_resync_tagging_finalize_recurse(Main *bmain, id->lib); lib_override_resync_tagging_finalize_recurse(bmain, id_from, library_indirect_level); } - break; } } -- cgit v1.2.3 From 7197017ea951eadddeea5a7b3941cabee2b960db Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 4 Jun 2021 01:15:15 +1000 Subject: WM: only use the tablet drag threshold for mouse button events Keyboard click-drag events now use the "Drag Threshold". This resolves a problem where keyboard click drag events used a much smaller threshold when using a tablet. --- source/blender/windowmanager/intern/wm_event_query.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 3efff20f107..0050c834a56 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -282,15 +282,17 @@ bool WM_event_is_mouse_drag(const wmEvent *event) int WM_event_drag_threshold(const struct wmEvent *event) { int drag_threshold; - if (WM_event_is_tablet(event)) { - drag_threshold = U.drag_threshold_tablet; - } - else if (ISMOUSE(event->prevtype)) { + if (ISMOUSE(event->prevtype)) { BLI_assert(event->prevtype != MOUSEMOVE); /* Using the previous type is important is we want to check the last pressed/released button, * The `event->type` would include #MOUSEMOVE which is always the case when dragging * and does not help us know which threshold to use. */ - drag_threshold = U.drag_threshold_mouse; + if (WM_event_is_tablet(event)) { + drag_threshold = U.drag_threshold_tablet; + } + else { + drag_threshold = U.drag_threshold_mouse; + } } else { /* Typically keyboard, could be NDOF button or other less common types. */ -- cgit v1.2.3 From bb16f9697301e8f8aba8f0016fd506706ba37afc Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Mon, 1 Jun 2020 00:20:04 +0100 Subject: GHOST/wayland: set swap interval to 0 The Wayland server will not update hidden surfaces. This will block the main event loop and thus also block updates to visible windows in a multi- window setup. --- intern/ghost/intern/GHOST_WindowWayland.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 1e1d0814d3a..e28ad805741 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -192,6 +192,9 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, if (setDrawingContextType(type) == GHOST_kFailure) { GHOST_PRINT("Failed to create EGL context" << std::endl); } + + /* set swap interval to 0 to prevent blocking */ + setSwapInterval(0); } GHOST_TSuccess GHOST_WindowWayland::close() -- cgit v1.2.3 From b5d7fb813f3b2a84059fa42f0005e87ef18883d7 Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Wed, 20 May 2020 22:42:43 +0100 Subject: cmake: use absolute path for Wayland libraries --- build_files/cmake/macros.cmake | 8 -------- build_files/cmake/platform/platform_unix.cmake | 14 ++++++-------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 813ac013cdf..5ae5ed7c29e 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -596,14 +596,6 @@ function(SETUP_LIBDIRS) link_directories(${GMP_LIBPATH}) endif() - if(WITH_GHOST_WAYLAND) - link_directories( - ${wayland-client_LIBRARY_DIRS} - ${wayland-egl_LIBRARY_DIRS} - ${xkbcommon_LIBRARY_DIRS} - ${wayland-cursor_LIBRARY_DIRS}) - endif() - if(WIN32 AND NOT UNIX) link_directories(${PTHREADS_LIBPATH}) endif() diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 7791112c6ab..dec12bde89c 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -578,14 +578,12 @@ if(WITH_GHOST_WAYLAND) set(WITH_GL_EGL ON) - if(WITH_GHOST_WAYLAND) - list(APPEND PLATFORM_LINKLIBS - ${wayland-client_LIBRARIES} - ${wayland-egl_LIBRARIES} - ${xkbcommon_LIBRARIES} - ${wayland-cursor_LIBRARIES} - ) - endif() + list(APPEND PLATFORM_LINKLIBS + ${wayland-client_LINK_LIBRARIES} + ${wayland-egl_LINK_LIBRARIES} + ${xkbcommon_LINK_LIBRARIES} + ${wayland-cursor_LINK_LIBRARIES} + ) endif() if(WITH_GHOST_X11) -- cgit v1.2.3 From efad9bcddadde370b677b549b16ec3267bbd8bad Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Sat, 12 Sep 2020 14:36:47 +0100 Subject: GHOST/wayland: inhibit warning --- intern/ghost/intern/GHOST_SystemWayland.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 81d9824ed26..6bfe3f34c4a 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -1481,7 +1481,7 @@ void GHOST_SystemWayland::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUn getMainDisplayDimensions(width, height); } -GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings glSettings) +GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*glSettings*/) { /* Create new off-screen window. */ wl_surface *os_surface = wl_compositor_create_surface(compositor()); -- cgit v1.2.3 From d9aae38bc8377f4cb99a1fbd2d92e367fcfceac6 Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Wed, 26 May 2021 20:57:32 +0100 Subject: GHOST/wayland: get cursor settings via D-Bus --- build_files/cmake/platform/platform_unix.cmake | 2 + intern/ghost/CMakeLists.txt | 1 + intern/ghost/intern/GHOST_SystemWayland.cpp | 12 +- intern/ghost/intern/GHOST_WaylandCursorSettings.h | 130 ++++++++++++++++++++++ 4 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 intern/ghost/intern/GHOST_WaylandCursorSettings.h diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index dec12bde89c..fcfde5a07ba 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -575,6 +575,7 @@ if(WITH_GHOST_WAYLAND) pkg_check_modules(wayland-scanner REQUIRED wayland-scanner) pkg_check_modules(xkbcommon REQUIRED xkbcommon) pkg_check_modules(wayland-cursor REQUIRED wayland-cursor) + pkg_check_modules(dbus REQUIRED dbus-1) set(WITH_GL_EGL ON) @@ -583,6 +584,7 @@ if(WITH_GHOST_WAYLAND) ${wayland-egl_LINK_LIBRARIES} ${xkbcommon_LINK_LIBRARIES} ${wayland-cursor_LINK_LIBRARIES} + ${dbus_LINK_LIBRARIES} ) endif() diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index f90e8a973bf..16929a13840 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -282,6 +282,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) ${wayland-egl_INCLUDE_DIRS} ${xkbcommon_INCLUDE_DIRS} ${wayland-cursor_INCLUDE_DIRS} + ${dbus_INCLUDE_DIRS} ) list(APPEND SRC diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 6bfe3f34c4a..97a2f7501a3 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -40,6 +40,7 @@ #include #include +#include "GHOST_WaylandCursorSettings.h" #include #include #include @@ -1336,11 +1337,14 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) } } - const char *theme = std::getenv("XCURSOR_THEME"); - const char *size = std::getenv("XCURSOR_SIZE"); - const int sizei = size ? std::stoi(size) : default_cursor_size; + std::string theme; + int size; + if (!get_cursor_settings(theme, size)) { + theme = std::string(); + size = default_cursor_size; + } - d->cursor_theme = wl_cursor_theme_load(theme, sizei, d->shm); + d->cursor_theme = wl_cursor_theme_load(theme.c_str(), size, d->shm); if (!d->cursor_theme) { display_destroy(d); throw std::runtime_error("Wayland: unable to access cursor themes!"); diff --git a/intern/ghost/intern/GHOST_WaylandCursorSettings.h b/intern/ghost/intern/GHOST_WaylandCursorSettings.h new file mode 100644 index 00000000000..c7d2d82ff84 --- /dev/null +++ b/intern/ghost/intern/GHOST_WaylandCursorSettings.h @@ -0,0 +1,130 @@ +/* + * 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. + */ + +/** \file + * \ingroup GHOST + */ + +#pragma once +#include +#include + +static DBusMessage *get_setting_sync(DBusConnection *const connection, + const char *key, + const char *value) +{ + DBusError error; + dbus_bool_t success; + DBusMessage *message; + DBusMessage *reply; + + dbus_error_init(&error); + + message = dbus_message_new_method_call("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", + "Read"); + + success = dbus_message_append_args( + message, DBUS_TYPE_STRING, &key, DBUS_TYPE_STRING, &value, DBUS_TYPE_INVALID); + + if (!success) { + return NULL; + } + + reply = dbus_connection_send_with_reply_and_block( + connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error); + + dbus_message_unref(message); + + if (dbus_error_is_set(&error)) { + return NULL; + } + + return reply; +} + +static bool parse_type(DBusMessage *const reply, const int type, void *value) +{ + DBusMessageIter iter[3]; + + dbus_message_iter_init(reply, &iter[0]); + if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) { + return false; + } + + dbus_message_iter_recurse(&iter[0], &iter[1]); + if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) { + return false; + } + + dbus_message_iter_recurse(&iter[1], &iter[2]); + if (dbus_message_iter_get_arg_type(&iter[2]) != type) { + return false; + } + + dbus_message_iter_get_basic(&iter[2], value); + + return true; +} + +static bool get_cursor_settings(std::string &theme, int &size) +{ + static const char name[] = "org.gnome.desktop.interface"; + static const char key_theme[] = "cursor-theme"; + static const char key_size[] = "cursor-size"; + + DBusError error; + DBusConnection *connection; + DBusMessage *reply; + const char *value_theme = NULL; + + dbus_error_init(&error); + + connection = dbus_bus_get(DBUS_BUS_SESSION, &error); + + if (dbus_error_is_set(&error)) { + return false; + } + + reply = get_setting_sync(connection, name, key_theme); + if (!reply) { + return false; + } + + if (!parse_type(reply, DBUS_TYPE_STRING, &value_theme)) { + dbus_message_unref(reply); + return false; + } + + theme = std::string(value_theme); + + dbus_message_unref(reply); + + reply = get_setting_sync(connection, name, key_size); + if (!reply) { + return false; + } + + if (!parse_type(reply, DBUS_TYPE_INT32, &size)) { + dbus_message_unref(reply); + return false; + } + + dbus_message_unref(reply); + + return true; +} -- cgit v1.2.3 From 1fd653dd823e88fede039cc304341263642cec55 Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Sat, 22 May 2021 16:41:58 +0100 Subject: GHOST/wayland: handle missing relative-pointer and pointer-constraints support --- intern/ghost/intern/GHOST_SystemWayland.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 97a2f7501a3..f1d867da884 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -1739,6 +1739,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible) GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode, wl_surface *surface) { + /* ignore, if the required protocols are not supported */ + if (!d->relative_pointer_manager || !d->pointer_constraints) { + return GHOST_kFailure; + } + if (d->inputs.empty()) { return GHOST_kFailure; } -- cgit v1.2.3 From e0bc5c4087f961db7504959c4eb09c04bda0ff5c Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Sun, 23 May 2021 16:21:02 +0100 Subject: GHOST/wayland: set parent relation only for dialogs to mimic X11 behaviour --- intern/ghost/intern/GHOST_WindowWayland.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index e28ad805741..1412c496512 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -171,7 +171,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, xdg_surface_add_listener(w->xdg_surface, &surface_listener, w); xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w); - if (parentWindow) { + if (parentWindow && is_dialog) { xdg_toplevel_set_parent( w->xdg_toplevel, dynamic_cast(parentWindow)->w->xdg_toplevel); } -- cgit v1.2.3 From 8b78510fc47a6c89036d371dfb8693cb851c61ab Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Mon, 24 May 2021 00:51:00 +0100 Subject: GHOST/wayland: handle return values for data sources --- intern/ghost/intern/GHOST_SystemWayland.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index f1d867da884..d6e46f27918 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -479,7 +479,9 @@ static void dnd_events(const input_t *const input, const GHOST_TEventType event) static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive) { int pipefd[2]; - pipe(pipefd); + if (pipe(pipefd) != 0) { + return {}; + } wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]); close(pipefd[1]); @@ -514,7 +516,9 @@ static void data_source_send(void *data, int32_t fd) { const char *const buffer = static_cast(data); - write(fd, buffer, strlen(buffer) + 1); + if (write(fd, buffer, strlen(buffer) + 1) < 0) { + GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl); + } close(fd); } -- cgit v1.2.3 From 870bcf6e1af5a493b2e2583d98b3b12b7ab3a05d Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Sat, 25 Jul 2020 15:58:29 +0100 Subject: GHOST/wayland: adapt window and cursor surface scale to support HiDPI screens --- intern/ghost/intern/GHOST_SystemWayland.cpp | 228 ++++++++++++++++++++-------- intern/ghost/intern/GHOST_SystemWayland.h | 14 ++ intern/ghost/intern/GHOST_WindowWayland.cpp | 99 +++++++++++- intern/ghost/intern/GHOST_WindowWayland.h | 17 +++ 4 files changed, 288 insertions(+), 70 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index d6e46f27918..1e9a2940c32 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -53,15 +53,6 @@ #include -struct output_t { - struct wl_output *output; - int32_t width, height; - int transform; - int scale; - std::string make; - std::string model; -}; - struct buffer_t { void *data; size_t size; @@ -73,6 +64,12 @@ struct cursor_t { struct wl_buffer *buffer; struct wl_cursor_image image; struct buffer_t *file_buffer = nullptr; + struct wl_cursor_theme *theme = nullptr; + int size; + std::string theme_name; + // outputs on which the cursor is visible + std::unordered_set outputs; + int scale = 1; }; struct data_offer_t { @@ -146,7 +143,10 @@ struct display_t { struct wl_shm *shm = nullptr; std::vector outputs; std::vector inputs; - struct wl_cursor_theme *cursor_theme = nullptr; + struct { + std::string theme; + int size; + } cursor; struct wl_data_device_manager *data_device_manager = nullptr; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr; struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr; @@ -155,6 +155,8 @@ struct display_t { std::vector os_egl_windows; }; +static GHOST_WindowManager *window_manager = nullptr; + static void display_destroy(display_t *d) { if (d->data_device_manager) { @@ -189,6 +191,9 @@ static void display_destroy(display_t *d) if (input->cursor.surface) { wl_surface_destroy(input->cursor.surface); } + if (input->cursor.theme) { + wl_cursor_theme_destroy(input->cursor.theme); + } if (input->pointer) { wl_pointer_destroy(input->pointer); } @@ -211,10 +216,6 @@ static void display_destroy(display_t *d) delete input; } - if (d->cursor_theme) { - wl_cursor_theme_destroy(d->cursor_theme); - } - if (d->shm) { wl_shm_destroy(d->shm); } @@ -801,6 +802,69 @@ const struct wl_buffer_listener cursor_buffer_listener = { cursor_buffer_release, }; +static GHOST_IWindow *get_window(struct wl_surface *surface) +{ + if (!surface) { + return nullptr; + } + + for (GHOST_IWindow *win : window_manager->getWindows()) { + if (surface == static_cast(win)->surface()) { + return win; + } + } + return nullptr; +} + +static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) +{ + int scale = 0; + for (const output_t *output : cursor.outputs) { + if (output->scale > scale) + scale = output->scale; + } + + if (scale > 0 && cursor.scale != scale) { + cursor.scale = scale; + wl_surface_set_buffer_scale(cursor.surface, scale); + wl_cursor_theme_destroy(cursor.theme); + cursor.theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); + return true; + } + return false; +} + +static void cursor_surface_enter(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) +{ + input_t *input = static_cast(data); + for (const output_t *reg_output : input->system->outputs()) { + if (reg_output->output == output) { + input->cursor.outputs.insert(reg_output); + } + } + update_cursor_scale(input->cursor, input->system->shm()); +} + +static void cursor_surface_leave(void *data, + struct wl_surface * /*wl_surface*/, + struct wl_output *output) +{ + input_t *input = static_cast(data); + for (const output_t *reg_output : input->system->outputs()) { + if (reg_output->output == output) { + input->cursor.outputs.erase(reg_output); + } + } + update_cursor_scale(input->cursor, input->system->shm()); +} + +struct wl_surface_listener cursor_surface_listener = { + cursor_surface_enter, + cursor_surface_leave, +}; + static void pointer_enter(void *data, struct wl_pointer * /*wl_pointer*/, uint32_t serial, @@ -808,22 +872,28 @@ static void pointer_enter(void *data, wl_fixed_t surface_x, wl_fixed_t surface_y) { - if (!surface) { + GHOST_WindowWayland *win = static_cast(get_window(surface)); + + if (!win) { return; } + + win->activate(); + input_t *input = static_cast(data); input->pointer_serial = serial; - input->x = wl_fixed_to_int(surface_x); - input->y = wl_fixed_to_int(surface_y); + input->x = win->scale() * wl_fixed_to_int(surface_x); + input->y = win->scale() * wl_fixed_to_int(surface_y); input->focus_pointer = surface; - input->system->pushEvent( - new GHOST_EventCursor(input->system->getMilliSeconds(), - GHOST_kEventCursorMove, - static_cast(wl_surface_get_user_data(surface)), - input->x, - input->y, - GHOST_TABLET_DATA_NONE)); + win->setCursorShape(win->getCursorShape()); + + input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + static_cast(win), + input->x, + input->y, + GHOST_TABLET_DATA_NONE)); } static void pointer_leave(void *data, @@ -831,9 +901,14 @@ static void pointer_leave(void *data, uint32_t /*serial*/, struct wl_surface *surface) { - if (surface != nullptr) { - static_cast(data)->focus_pointer = nullptr; + GHOST_IWindow *win = get_window(surface); + + if (!win) { + return; } + + static_cast(data)->focus_pointer = nullptr; + static_cast(win)->deactivate(); } static void pointer_motion(void *data, @@ -844,21 +919,20 @@ static void pointer_motion(void *data, { input_t *input = static_cast(data); - GHOST_IWindow *win = static_cast( - wl_surface_get_user_data(input->focus_pointer)); + GHOST_WindowWayland *win = static_cast(get_window(input->focus_pointer)); if (!win) { return; } - input->x = wl_fixed_to_int(surface_x); - input->y = wl_fixed_to_int(surface_y); + input->x = win->scale() * wl_fixed_to_int(surface_x); + input->y = win->scale() * wl_fixed_to_int(surface_y); input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(), GHOST_kEventCursorMove, win, - wl_fixed_to_int(surface_x), - wl_fixed_to_int(surface_y), + input->x, + input->y, GHOST_TABLET_DATA_NONE)); } @@ -869,6 +943,14 @@ static void pointer_button(void *data, uint32_t button, uint32_t state) { + input_t *input = static_cast(data); + + GHOST_IWindow *win = get_window(input->focus_pointer); + + if (!win) { + return; + } + GHOST_TEventType etype = GHOST_kEventUnknown; switch (state) { case WL_POINTER_BUTTON_STATE_RELEASED: @@ -892,9 +974,6 @@ static void pointer_button(void *data, break; } - input_t *input = static_cast(data); - GHOST_IWindow *win = static_cast( - wl_surface_get_user_data(input->focus_pointer)); input->data_source->source_serial = serial; input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); input->system->pushEvent(new GHOST_EventButton( @@ -907,12 +986,18 @@ static void pointer_axis(void *data, uint32_t axis, wl_fixed_t value) { + input_t *input = static_cast(data); + + GHOST_IWindow *win = get_window(input->focus_pointer); + + if (!win) { + return; + } + if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) { return; } - input_t *input = static_cast(data); - GHOST_IWindow *win = static_cast( - wl_surface_get_user_data(input->focus_pointer)); + input->system->pushEvent( new GHOST_EventWheel(input->system->getMilliSeconds(), win, std::signbit(value) ? +1 : -1)); } @@ -1142,7 +1227,12 @@ static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capa input->cursor.visible = true; input->cursor.buffer = nullptr; input->cursor.file_buffer = new buffer_t; + if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) { + input->cursor.theme_name = std::string(); + input->cursor.size = default_cursor_size; + } wl_pointer_add_listener(input->pointer, &pointer_listener, data); + wl_surface_add_listener(input->cursor.surface, &cursor_surface_listener, data); } if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { @@ -1165,8 +1255,8 @@ static void output_geometry(void *data, struct wl_output * /*wl_output*/, int32_t /*x*/, int32_t /*y*/, - int32_t /*physical_width*/, - int32_t /*physical_height*/, + int32_t physical_width, + int32_t physical_height, int32_t /*subpixel*/, const char *make, const char *model, @@ -1176,6 +1266,8 @@ static void output_geometry(void *data, output->transform = transform; output->make = std::string(make); output->model = std::string(model); + output->width_mm = physical_width; + output->height_mm = physical_height; } static void output_mode(void *data, @@ -1186,8 +1278,8 @@ static void output_mode(void *data, int32_t /*refresh*/) { output_t *output = static_cast(data); - output->width = width; - output->height = height; + output->width_pxl = width; + output->height_pxl = height; } /** @@ -1232,7 +1324,7 @@ static void global_add(void *data, struct display_t *display = static_cast(data); if (!strcmp(interface, wl_compositor_interface.name)) { display->compositor = static_cast( - wl_registry_bind(wl_registry, name, &wl_compositor_interface, 1)); + wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3)); } else if (!strcmp(interface, xdg_wm_base_interface.name)) { display->xdg_shell = static_cast( @@ -1340,19 +1432,6 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) wl_data_device_add_listener(input->data_device, &data_device_listener, input); } } - - std::string theme; - int size; - if (!get_cursor_settings(theme, size)) { - theme = std::string(); - size = default_cursor_size; - } - - d->cursor_theme = wl_cursor_theme_load(theme.c_str(), size, d->shm); - if (!d->cursor_theme) { - display_destroy(d); - throw std::runtime_error("Wayland: unable to access cursor themes!"); - } } GHOST_SystemWayland::~GHOST_SystemWayland() @@ -1479,8 +1558,8 @@ void GHOST_SystemWayland::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TU { if (getNumDisplays() > 0) { /* We assume first output as main. */ - width = uint32_t(d->outputs[0]->width); - height = uint32_t(d->outputs[0]->height); + width = uint32_t(d->outputs[0]->width_pxl) / d->outputs[0]->scale; + height = uint32_t(d->outputs[0]->height_pxl) / d->outputs[0]->scale; } } @@ -1538,6 +1617,11 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title, const bool is_dialog, const GHOST_IWindow *parentWindow) { + /* globally store pointer to window manager */ + if (!window_manager) { + window_manager = getWindowManager(); + } + GHOST_WindowWayland *window = new GHOST_WindowWayland( this, title, @@ -1582,6 +1666,16 @@ xdg_wm_base *GHOST_SystemWayland::shell() return d->xdg_shell; } +const std::vector &GHOST_SystemWayland::outputs() const +{ + return d->outputs; +} + +wl_shm *GHOST_SystemWayland::shm() const +{ + return d->shm; +} + void GHOST_SystemWayland::setSelection(const std::string &selection) { this->selection = selection; @@ -1603,8 +1697,8 @@ static void set_cursor_buffer(input_t *input, wl_buffer *buffer) wl_pointer_set_cursor(input->pointer, input->pointer_serial, input->cursor.surface, - int32_t(input->cursor.image.hotspot_x), - int32_t(input->cursor.image.hotspot_y)); + int32_t(input->cursor.image.hotspot_x) / input->cursor.scale, + int32_t(input->cursor.image.hotspot_y) / input->cursor.scale); } } @@ -1616,7 +1710,15 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) const std::string cursor_name = cursors.count(shape) ? cursors.at(shape) : cursors.at(GHOST_kStandardCursorDefault); - wl_cursor *cursor = wl_cursor_theme_get_cursor(d->cursor_theme, cursor_name.c_str()); + input_t *input = d->inputs[0]; + cursor_t *c = &input->cursor; + + if (!c->theme) { + /* The cursor surface hasn't entered an output yet. Initialise theme with scale 1. */ + c->theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->inputs[0]->system->shm()); + } + + wl_cursor *cursor = wl_cursor_theme_get_cursor(c->theme, cursor_name.c_str()); if (!cursor) { GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl); @@ -1628,11 +1730,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) if (!buffer) { return GHOST_kFailure; } - cursor_t *c = &d->inputs[0]->cursor; + c->buffer = buffer; c->image = *image; - set_cursor_buffer(d->inputs[0], buffer); + set_cursor_buffer(input, buffer); return GHOST_kSuccess; } diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 10b9ef6bd62..2457bed5def 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -34,6 +34,16 @@ class GHOST_WindowWayland; struct display_t; +struct output_t { + struct wl_output *output; + int32_t width_pxl, height_pxl; // dimensions in pixel + int32_t width_mm, height_mm; // dimensions in millimeter + int transform; + int scale; + std::string make; + std::string model; +}; + class GHOST_SystemWayland : public GHOST_System { public: GHOST_SystemWayland(); @@ -84,6 +94,10 @@ class GHOST_SystemWayland : public GHOST_System { xdg_wm_base *shell(); + const std::vector &outputs() const; + + wl_shm *shm() const; + void setSelection(const std::string &selection); GHOST_TSuccess setCursorShape(GHOST_TStandardCursor shape); diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 1412c496512..70cfe35d628 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -29,9 +29,15 @@ #include +static constexpr size_t base_dpi = 96; + struct window_t { GHOST_WindowWayland *w; wl_surface *surface; + // outputs on which the window is currently shown on + std::unordered_set outputs; + GHOST_TUns16 dpi = 0; + int scale = 1; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; wl_egl_window *egl_window; @@ -97,13 +103,14 @@ static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t ser { window_t *win = static_cast(data); - int w, h; - wl_egl_window_get_attached_size(win->egl_window, &w, &h); - if (win->pending_width != 0 && win->pending_height != 0 && win->pending_width != w && - win->pending_height != h) { - win->width = win->pending_width; - win->height = win->pending_height; - wl_egl_window_resize(win->egl_window, win->pending_width, win->pending_height, 0, 0); + if (win->xdg_surface != xdg_surface) { + return; + } + + if (win->pending_width != 0 && win->pending_height != 0) { + win->width = win->scale * win->pending_width; + win->height = win->scale * win->pending_height; + wl_egl_window_resize(win->egl_window, win->width, win->height, 0, 0); win->pending_width = 0; win->pending_height = 0; win->w->notify_size(); @@ -123,6 +130,52 @@ static const xdg_surface_listener surface_listener = { surface_configure, }; +static bool update_scale(GHOST_WindowWayland *window) +{ + int scale = 0; + for (const output_t *output : window->outputs_active()) { + if (output->scale > scale) + scale = output->scale; + } + + if (scale > 0 && window->scale() != scale) { + window->scale() = scale; + // using the real DPI will cause wrong scaling of the UI + // use a multiplier for the default DPI as workaround + window->dpi() = scale * base_dpi; + wl_surface_set_buffer_scale(window->surface(), scale); + return true; + } + return false; +} + +static void surface_enter(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output) +{ + GHOST_WindowWayland *w = static_cast(data); + for (const output_t *reg_output : w->outputs()) { + if (reg_output->output == output) { + w->outputs_active().insert(reg_output); + } + } + update_scale(w); +} + +static void surface_leave(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output) +{ + GHOST_WindowWayland *w = static_cast(data); + for (const output_t *reg_output : w->outputs()) { + if (reg_output->output == output) { + w->outputs_active().erase(reg_output); + } + } + update_scale(w); +} + +struct wl_surface_listener wl_surface_listener = { + surface_enter, + surface_leave, +}; + /** \} */ /* -------------------------------------------------------------------- */ @@ -161,6 +214,8 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, /* Window surfaces. */ w->surface = wl_compositor_create_surface(m_system->compositor()); + wl_surface_add_listener(w->surface, &wl_surface_listener, this); + w->egl_window = wl_egl_window_create(w->surface, int(width), int(height)); w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface); @@ -229,6 +284,31 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size() new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this)); } +wl_surface *GHOST_WindowWayland::surface() const +{ + return w->surface; +} + +const std::vector &GHOST_WindowWayland::outputs() const +{ + return m_system->outputs(); +} + +std::unordered_set &GHOST_WindowWayland::outputs_active() +{ + return w->outputs; +} + +uint16_t &GHOST_WindowWayland::dpi() +{ + return w->dpi; +} + +int &GHOST_WindowWayland::scale() +{ + return w->scale; +} + GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode) { return m_system->setCursorGrab(mode, w->surface); @@ -320,6 +400,11 @@ GHOST_WindowWayland::~GHOST_WindowWayland() delete w; } +GHOST_TUns16 GHOST_WindowWayland::getDPIHint() +{ + return w->dpi; +} + GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible) { return m_system->setCursorVisibility(visible); diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h index b62b5c24d60..dbddc7c469e 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.h +++ b/intern/ghost/intern/GHOST_WindowWayland.h @@ -24,9 +24,14 @@ #include "GHOST_Window.h" +#include +#include + class GHOST_SystemWayland; struct window_t; +struct wl_surface; +struct output_t; class GHOST_WindowWayland : public GHOST_Window { public: @@ -47,6 +52,8 @@ class GHOST_WindowWayland : public GHOST_Window { ~GHOST_WindowWayland() override; + GHOST_TUns16 getDPIHint() override; + GHOST_TSuccess close(); GHOST_TSuccess activate(); @@ -55,6 +62,16 @@ class GHOST_WindowWayland : public GHOST_Window { GHOST_TSuccess notify_size(); + wl_surface *surface() const; + + const std::vector &outputs() const; + + std::unordered_set &outputs_active(); + + uint16_t &dpi(); + + int &scale(); + protected: GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override; -- cgit v1.2.3 From 72607feb9180c76bf8f9d149100af0e8fbefb32d Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Sat, 22 May 2021 18:20:35 +0100 Subject: GHOST/wayland: add 'xdg-decoration' support --- intern/ghost/CMakeLists.txt | 5 +++++ intern/ghost/intern/GHOST_SystemWayland.cpp | 14 ++++++++++++++ intern/ghost/intern/GHOST_SystemWayland.h | 3 +++ intern/ghost/intern/GHOST_WindowWayland.cpp | 26 ++++++++++++++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 16929a13840..1b5cdb3cda0 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -322,6 +322,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) xdg-shell "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml" ) + # xdg-decoration. + generate_protocol_bindings( + xdg-decoration + "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + ) # Pointer-constraints. generate_protocol_bindings( pointer-constraints diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 1e9a2940c32..02785181b52 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -140,6 +140,7 @@ struct display_t { struct wl_display *display; struct wl_compositor *compositor = nullptr; struct xdg_wm_base *xdg_shell = nullptr; + struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr; struct wl_shm *shm = nullptr; std::vector outputs; std::vector inputs; @@ -240,6 +241,10 @@ static void display_destroy(display_t *d) wl_compositor_destroy(d->compositor); } + if (d->xdg_decoration_manager) { + zxdg_decoration_manager_v1_destroy(d->xdg_decoration_manager); + } + if (d->xdg_shell) { xdg_wm_base_destroy(d->xdg_shell); } @@ -1331,6 +1336,10 @@ static void global_add(void *data, wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1)); xdg_wm_base_add_listener(display->xdg_shell, &shell_listener, nullptr); } + else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name)) { + display->xdg_decoration_manager = static_cast( + wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1)); + } else if (!strcmp(interface, wl_output_interface.name)) { output_t *output = new output_t; output->scale = 1; @@ -1666,6 +1675,11 @@ xdg_wm_base *GHOST_SystemWayland::shell() return d->xdg_shell; } +zxdg_decoration_manager_v1 *GHOST_SystemWayland::decoration_manager() +{ + return d->xdg_decoration_manager; +} + const std::vector &GHOST_SystemWayland::outputs() const { return d->outputs; diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 2457bed5def..3a08a0d3b16 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -26,6 +26,7 @@ #include "GHOST_WindowWayland.h" #include +#include #include #include @@ -94,6 +95,8 @@ class GHOST_SystemWayland : public GHOST_System { xdg_wm_base *shell(); + zxdg_decoration_manager_v1 *decoration_manager(); + const std::vector &outputs() const; wl_shm *shm() const; diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 70cfe35d628..cbac2d6eaa1 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -40,6 +40,8 @@ struct window_t { int scale = 1; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; + struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr; + enum zxdg_toplevel_decoration_v1_mode decoration_mode; wl_egl_window *egl_window; int32_t pending_width, pending_height; bool is_maximised; @@ -99,6 +101,18 @@ static const xdg_toplevel_listener toplevel_listener = { toplevel_close, }; +static void toplevel_decoration_configure( + void *data, + struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/, + uint32_t mode) +{ + static_cast(data)->decoration_mode = zxdg_toplevel_decoration_v1_mode(mode); +} + +static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = { + toplevel_decoration_configure, +}; + static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) { window_t *win = static_cast(data); @@ -221,6 +235,15 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface); w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface); + if (m_system->decoration_manager()) { + w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + m_system->decoration_manager(), w->xdg_toplevel); + zxdg_toplevel_decoration_v1_add_listener( + w->xdg_toplevel_decoration, &toplevel_decoration_v1_listener, w); + zxdg_toplevel_decoration_v1_set_mode(w->xdg_toplevel_decoration, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + wl_surface_set_user_data(w->surface, this); xdg_surface_add_listener(w->xdg_surface, &surface_listener, w); @@ -393,6 +416,9 @@ GHOST_WindowWayland::~GHOST_WindowWayland() releaseNativeHandles(); wl_egl_window_destroy(w->egl_window); + if (w->xdg_toplevel_decoration) { + zxdg_toplevel_decoration_v1_destroy(w->xdg_toplevel_decoration); + } xdg_toplevel_destroy(w->xdg_toplevel); xdg_surface_destroy(w->xdg_surface); wl_surface_destroy(w->surface); -- cgit v1.2.3 From 899eefd1bbe968cd7ee05f8d16be95076b5395e3 Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Mon, 24 May 2021 22:10:58 +0100 Subject: GHOST/wayland: fix non-moving normal cursor --- intern/ghost/intern/GHOST_SystemWayland.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 02785181b52..7b035879b9f 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -1883,6 +1883,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo break; case GHOST_kGrabNormal: + break; case GHOST_kGrabWrap: input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( d->relative_pointer_manager, input->pointer); -- cgit v1.2.3 From b414322f2632c761733ef2e9b65ce745f2db9e04 Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Mon, 24 May 2021 22:49:53 +0100 Subject: GHOST/wayland: fix restoring hidden cursor --- intern/ghost/intern/GHOST_SystemWayland.cpp | 33 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 7b035879b9f..1d2a349fdf1 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -800,7 +800,11 @@ static void cursor_buffer_release(void *data, struct wl_buffer *wl_buffer) cursor_t *cursor = static_cast(data); wl_buffer_destroy(wl_buffer); - cursor->buffer = nullptr; + + if (wl_buffer == cursor->buffer) { + /* the mapped buffer was from a custom cursor */ + cursor->buffer = nullptr; + } } const struct wl_buffer_listener cursor_buffer_listener = { @@ -1697,23 +1701,20 @@ void GHOST_SystemWayland::setSelection(const std::string &selection) static void set_cursor_buffer(input_t *input, wl_buffer *buffer) { - input->cursor.visible = (buffer != nullptr); + cursor_t *c = &input->cursor; - wl_surface_attach(input->cursor.surface, buffer, 0, 0); - wl_surface_commit(input->cursor.surface); + c->visible = (buffer != nullptr); - if (input->cursor.visible) { - wl_surface_damage(input->cursor.surface, - 0, - 0, - int32_t(input->cursor.image.width), - int32_t(input->cursor.image.height)); - wl_pointer_set_cursor(input->pointer, - input->pointer_serial, - input->cursor.surface, - int32_t(input->cursor.image.hotspot_x) / input->cursor.scale, - int32_t(input->cursor.image.hotspot_y) / input->cursor.scale); - } + wl_surface_attach(c->surface, buffer, 0, 0); + + wl_surface_damage(c->surface, 0, 0, int32_t(c->image.width), int32_t(c->image.height)); + wl_pointer_set_cursor(input->pointer, + input->pointer_serial, + c->visible ? c->surface : nullptr, + int32_t(c->image.hotspot_x) / c->scale, + int32_t(c->image.hotspot_y) / c->scale); + + wl_surface_commit(c->surface); } GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) -- cgit v1.2.3 From a1063fc6c250a7a7016b11064403de5000c301dc Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 3 Jun 2021 19:39:28 +0200 Subject: VSE: Remove JPEG reference from proxy panel Proxies doesn't use MJPEG codec anymore, but text still referenced it. --- release/scripts/startup/bl_ui/space_sequencer.py | 2 +- source/blender/makesrna/intern/rna_sequencer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index e9bfe6cd4e2..c5284f9911e 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1946,7 +1946,7 @@ class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel): layout.prop(proxy, "use_overwrite") col = layout.column() - col.prop(proxy, "quality", text="Build JPEG Quality") + col.prop(proxy, "quality", text="Quality") if strip.type == 'MOVIE': col = layout.column() diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 8fbad449cf6..4dba82443d6 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1518,7 +1518,7 @@ static void rna_def_strip_proxy(BlenderRNA *brna) prop = RNA_def_property(srna, "quality", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "quality"); - RNA_def_property_ui_text(prop, "Quality", "JPEG Quality of proxies to build"); + RNA_def_property_ui_text(prop, "Quality", "Quality of proxies to build"); RNA_def_property_ui_range(prop, 1, 100, 1, -1); prop = RNA_def_property(srna, "timecode", PROP_ENUM, PROP_NONE); -- cgit v1.2.3 From 3e695a27cdfad560d0b28e742cfa069d098200d6 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 3 Jun 2021 19:58:36 +0200 Subject: VSE: Add refresh_all operator to all sequencer regions This operator is needed in some cases to update image preview. In workspaces with smaller timelines this is limiting, because users need to first check that mouse cursor is in correct place, then press CTRL+R shortcut. --- release/scripts/presets/keyconfig/keymap_data/blender_default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index c476d488724..841f9721111 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2543,6 +2543,7 @@ def km_sequencercommon(params): {"properties": [("data_path", 'scene.sequence_editor.show_overlay')]}), ("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, {"properties": [("data_path", 'space_data.view_type'), ("value_1", 'SEQUENCER'), ("value_2", 'PREVIEW')]}), + ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ]) if params.select_mouse == 'LEFTMOUSE' and not params.legacy: @@ -2583,7 +2584,6 @@ def km_sequencer(params): ("sequencer.reload", {"type": 'R', "value": 'PRESS', "alt": True}, None), ("sequencer.reload", {"type": 'R', "value": 'PRESS', "shift": True, "alt": True}, {"properties": [("adjust_length", True)]}), - ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ("sequencer.offset_clear", {"type": 'O', "value": 'PRESS', "alt": True}, None), ("sequencer.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), ("sequencer.delete", {"type": 'X', "value": 'PRESS'}, None), -- cgit v1.2.3 From dba3fb9e091acfa7d2144468a220b2b0d577c15d Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Tue, 18 May 2021 01:03:01 +0200 Subject: Overlay: Flash on Mode Transfer overlay This implements T87633 This overlay renders a flash animation on the target object when transfering the mode to it using the mode transfer operator. This provides visual feedback when switching between objects without extra overlays that affect the general color and lighting in the scene. Differences with the design task: - This uses just a fade out animation instead of a fade in/out animation. The code is ready for fade in/out, but as the rest of the overlays (face sets, masks...) change instantly without animation, having a fade in/out effect gives the impression that the object flashes twice (once for the face sets, twice for the peak alpha of the flash animation). - The rendering uses a flat color without fresnel for now, but this can be improved in the future to make it look more like the shader in the prototype. - Not enabled by default (can be enabled in the overlays panel), maybe the defaults can change for 3.0 to disable fade inactive and enable this instead. Reviewed By: jbakker, JulienKaspar Differential Revision: https://developer.blender.org/D11055 --- release/scripts/startup/bl_ui/space_view3d.py | 3 + source/blender/draw/CMakeLists.txt | 1 + .../blender/draw/engines/overlay/overlay_engine.c | 8 ++ .../draw/engines/overlay/overlay_mode_transfer.c | 159 +++++++++++++++++++++ .../blender/draw/engines/overlay/overlay_private.h | 12 ++ source/blender/editors/object/object_modes.c | 11 ++ source/blender/makesdna/DNA_object_types.h | 3 + source/blender/makesdna/DNA_view3d_types.h | 1 + source/blender/makesrna/intern/rna_space.c | 8 ++ 9 files changed, 206 insertions(+) create mode 100644 source/blender/draw/engines/overlay/overlay_mode_transfer.c diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 229b520e449..df520b38eb0 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -6186,6 +6186,9 @@ class VIEW3D_PT_overlay_geometry(Panel): sub.active = overlay.show_fade_inactive sub.prop(overlay, "fade_inactive_alpha", text="Fade Inactive Geometry") + row = col.row(align=True) + row.prop(overlay, "show_mode_transfer", text="Flash on Mode Transfer") + col = layout.column(align=True) col.active = display_all diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index d6598bf79b0..ee5b2c549a5 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -149,6 +149,7 @@ set(SRC engines/overlay/overlay_image.c engines/overlay/overlay_lattice.c engines/overlay/overlay_metaball.c + engines/overlay/overlay_mode_transfer.c engines/overlay/overlay_motion_path.c engines/overlay/overlay_outline.c engines/overlay/overlay_paint.c diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index b8f721946f2..19f822e3f68 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -207,6 +207,7 @@ static void OVERLAY_cache_init(void *vedata) OVERLAY_armature_cache_init(vedata); OVERLAY_background_cache_init(vedata); OVERLAY_fade_cache_init(vedata); + OVERLAY_mode_transfer_cache_init(vedata); OVERLAY_extra_cache_init(vedata); OVERLAY_facing_cache_init(vedata); OVERLAY_gpencil_cache_init(vedata); @@ -323,6 +324,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) !is_select; const bool draw_fade = draw_surface && (pd->overlay.flag & V3D_OVERLAY_FADE_INACTIVE) && overlay_should_fade_object(ob, draw_ctx->obact); + const bool draw_mode_transfer = draw_surface && (pd->overlay.flag & V3D_OVERLAY_MODE_TRANSFER); const bool draw_bones = (pd->overlay.flag & V3D_OVERLAY_HIDE_BONES) == 0; const bool draw_wires = draw_surface && has_surface && (pd->wireframe_mode || !pd->hide_overlays); @@ -349,6 +351,9 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) if (draw_facing) { OVERLAY_facing_cache_populate(vedata, ob); } + if (draw_mode_transfer) { + OVERLAY_mode_transfer_cache_populate(vedata, ob); + } if (draw_wires) { OVERLAY_wireframe_cache_populate(vedata, ob, dupli, do_init); } @@ -504,6 +509,7 @@ static void OVERLAY_cache_finish(void *vedata) {GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); } + OVERLAY_mode_transfer_cache_finish(vedata); OVERLAY_antialiasing_cache_finish(vedata); OVERLAY_armature_cache_finish(vedata); OVERLAY_image_cache_finish(vedata); @@ -566,6 +572,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_image_draw(vedata); OVERLAY_fade_draw(vedata); OVERLAY_facing_draw(vedata); + OVERLAY_mode_transfer_draw(vedata); OVERLAY_extra_blend_draw(vedata); OVERLAY_volume_draw(vedata); @@ -605,6 +612,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_fade_infront_draw(vedata); OVERLAY_facing_infront_draw(vedata); + OVERLAY_mode_transfer_infront_draw(vedata); if (DRW_state_is_fbo()) { GPU_framebuffer_bind(fbl->overlay_line_in_front_fb); diff --git a/source/blender/draw/engines/overlay/overlay_mode_transfer.c b/source/blender/draw/engines/overlay/overlay_mode_transfer.c new file mode 100644 index 00000000000..253f606b086 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_mode_transfer.c @@ -0,0 +1,159 @@ +/* + * 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. + */ + +/** \file + * \ingroup draw_engine + */ + +#include "BKE_paint.h" +#include "DRW_render.h" + +#include "ED_view3d.h" + +#include "PIL_time.h" +#include "UI_resources.h" + +#include "overlay_private.h" + +void OVERLAY_mode_transfer_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + pd->mode_transfer.time = PIL_check_seconds_timer(); + + for (int i = 0; i < 2; i++) { + /* Non Meshes Pass (Camera, empties, lights ...) */ + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->mode_transfer_ps[i], state | pd->clipping_state); + } +} + +#define MODE_TRANSFER_FLASH_LENGTH 0.55f +/* TODO(pablodp606): Remove this option for 3.0 if fade in/out is not used. */ +#define MODE_TRANSFER_FLASH_FADE 0.0f +#define MODE_TRANSFER_FLASH_MAX_ALPHA 0.25f + +static bool mode_transfer_is_animation_running(const float anim_time) +{ + return anim_time >= 0.0f && anim_time <= MODE_TRANSFER_FLASH_LENGTH; +} + +static float mode_transfer_alpha_for_animation_time_get(const float anim_time) +{ + if (anim_time > MODE_TRANSFER_FLASH_LENGTH) { + return 0.0f; + } + + if (anim_time < 0.0f) { + return 0.0f; + } + + if (MODE_TRANSFER_FLASH_FADE <= 0.0f) { + return (1.0f - (anim_time / MODE_TRANSFER_FLASH_LENGTH)) * MODE_TRANSFER_FLASH_MAX_ALPHA; + } + + const float flash_fade_in_time = MODE_TRANSFER_FLASH_LENGTH * MODE_TRANSFER_FLASH_FADE; + const float flash_fade_out_time = MODE_TRANSFER_FLASH_LENGTH - flash_fade_in_time; + + float alpha = 0.0f; + if (anim_time < flash_fade_in_time) { + alpha = anim_time / flash_fade_in_time; + } + else { + const float fade_out_anim_time = anim_time - flash_fade_in_time; + alpha = 1.0f - (fade_out_anim_time / flash_fade_out_time); + } + + return alpha * MODE_TRANSFER_FLASH_MAX_ALPHA; +} + +void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + OVERLAY_PassList *psl = vedata->psl; + + if (pd->xray_enabled) { + return; + } + + const float animation_time = pd->mode_transfer.time - + ob->runtime.overlay_mode_transfer_start_time; + + if (!mode_transfer_is_animation_running(animation_time)) { + return; + } + + const DRWContextState *draw_ctx = DRW_context_state_get(); + const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && + !DRW_state_is_image_render(); + const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0; + + DRWShadingGroup *mode_transfer_grp[2]; + + for (int i = 0; i < 2; i++) { + GPUShader *sh = OVERLAY_shader_uniform_color(); + mode_transfer_grp[i] = DRW_shgroup_create(sh, psl->mode_transfer_ps[i]); + DRW_shgroup_uniform_block(mode_transfer_grp[i], "globalsBlock", G_draw.block_ubo); + + float color[4]; + UI_GetThemeColor3fv(TH_VERTEX_SELECT, color); + color[3] = mode_transfer_alpha_for_animation_time_get(animation_time); + srgb_to_linearrgb_v4(color, color); + DRW_shgroup_uniform_vec4_copy(mode_transfer_grp[i], "color", color); + } + + if (!pd->use_in_front) { + mode_transfer_grp[IN_FRONT] = mode_transfer_grp[NOT_IN_FRONT]; + } + + pd->mode_transfer.any_animated = true; + + if (use_sculpt_pbvh) { + DRW_shgroup_call_sculpt(mode_transfer_grp[is_xray], ob, false, false); + } + else { + struct GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom) { + DRW_shgroup_call(mode_transfer_grp[is_xray], geom, ob); + } + } +} + +void OVERLAY_mode_transfer_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->mode_transfer_ps[NOT_IN_FRONT]); +} + +void OVERLAY_mode_transfer_infront_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->mode_transfer_ps[IN_FRONT]); +} + +void OVERLAY_mode_transfer_cache_finish(OVERLAY_Data *vedata) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + if (pd->mode_transfer.any_animated) { + DRW_viewport_request_redraw(); + } + pd->mode_transfer.any_animated = false; +} diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index db43136e308..969289a3219 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -107,6 +107,7 @@ typedef struct OVERLAY_PassList { DRWPass *gpencil_canvas_ps; DRWPass *facing_ps[2]; DRWPass *fade_ps[2]; + DRWPass *mode_transfer_ps[2]; DRWPass *grid_ps; DRWPass *image_background_ps; DRWPass *image_background_scene_ps; @@ -282,6 +283,7 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *extra_grid_grp; DRWShadingGroup *facing_grp[2]; DRWShadingGroup *fade_grp[2]; + DRWShadingGroup *flash_grp[2]; DRWShadingGroup *motion_path_lines_grp; DRWShadingGroup *motion_path_points_grp; DRWShadingGroup *outlines_grp; @@ -414,6 +416,10 @@ typedef struct OVERLAY_PrivateData { struct { DRWCallBuffer *handle[2]; } mball; + struct { + double time; + bool any_animated; + } mode_transfer; } OVERLAY_PrivateData; /* Transient data */ typedef struct OVERLAY_StorageList { @@ -607,6 +613,12 @@ void OVERLAY_fade_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_fade_draw(OVERLAY_Data *vedata); void OVERLAY_fade_infront_draw(OVERLAY_Data *vedata); +void OVERLAY_mode_transfer_cache_init(OVERLAY_Data *vedata); +void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_mode_transfer_draw(OVERLAY_Data *vedata); +void OVERLAY_mode_transfer_infront_draw(OVERLAY_Data *vedata); +void OVERLAY_mode_transfer_cache_finish(OVERLAY_Data *vedata); + void OVERLAY_grid_init(OVERLAY_Data *vedata); void OVERLAY_grid_cache_init(OVERLAY_Data *vedata); void OVERLAY_grid_draw(OVERLAY_Data *vedata); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index edf9afd1fd6..3d1a5ac2d62 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -30,6 +30,8 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "PIL_time.h" + #include "BLT_translation.h" #include "BKE_context.h" @@ -438,6 +440,13 @@ static void object_transfer_mode_reposition_view_pivot(bContext *C, const int mv ups->last_stroke_valid = true; } +static void object_overlay_mode_transfer_animation_start(bContext *C, Object *ob_dst) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob_dst_eval = DEG_get_evaluated_object(depsgraph, ob_dst); + ob_dst_eval->runtime.overlay_mode_transfer_start_time = PIL_check_seconds_timer(); +} + static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base_dst) { Scene *scene = CTX_data_scene(C); @@ -475,6 +484,8 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base ob_dst_orig = DEG_get_original_object(ob_dst); ED_object_mode_set_ex(C, last_mode, true, op->reports); + object_overlay_mode_transfer_animation_start(C, ob_dst); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); WM_toolsystem_update_from_context_view3d(C); mode_transfered = true; diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index aa1178fb139..9951bdefbbb 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -144,6 +144,9 @@ typedef struct Object_Runtime { */ char is_data_eval_owned; + /** Start time of the mode transfer overlay animation. */ + double overlay_mode_transfer_start_time; + /** Axis aligned boundbox (in localspace). */ struct BoundBox *bb; diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 2f4e4e57b9f..9e7e30d913e 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -515,6 +515,7 @@ enum { V3D_OVERLAY_HIDE_OBJECT_ORIGINS = (1 << 10), V3D_OVERLAY_STATS = (1 << 11), V3D_OVERLAY_FADE_INACTIVE = (1 << 12), + V3D_OVERLAY_MODE_TRANSFER = (1 << 13), }; /** #View3DOverlay.edit_flag */ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 0af2572a4bd..bb356e4b532 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4129,6 +4129,14 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop, "Fade Inactive Objects", "Fade inactive geometry using the viewport background color"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "show_mode_transfer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_MODE_TRANSFER); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, + "Flash on Mode Transfer", + "Flash the target object when tranfering the active mode to it"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "fade_inactive_alpha", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.fade_alpha"); RNA_def_property_ui_text(prop, "Opacity", "Strength of the fade effect"); -- cgit v1.2.3 From c18675b12c66ccfc96079a67dc65a2eb978af7bd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 3 Jun 2021 18:06:14 -0400 Subject: Cleanup: Add comment explaining assert This triggers fairly often during development, so it might save some frustration at some point to have a comment here. --- source/blender/blenkernel/intern/attribute_access.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 1495eb23254..f2ad873b10e 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -658,6 +658,7 @@ GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name, std::optional CustomDataAttributes::get_for_write(const StringRef name) { + /* 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) { -- cgit v1.2.3 From e5a1cadb2f7a0a4e4ff4580e3d0865342afd0da7 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 3 Jun 2021 18:33:37 -0400 Subject: Geometry Nodes: Support curve data in the geometry delete node This commit implements support for deleting curve data in the geometry delete node. Spline domain and point domain attributes are supported. Differential Revision: https://developer.blender.org/D11464 --- .../geometry/nodes/node_geo_delete_geometry.cc | 151 +++++++++++++++++++++ 1 file changed, 151 insertions(+) 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 4692cef6616..0e053a6d0e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -26,6 +26,8 @@ #include "node_geometry_util.hh" +using blender::bke::CustomDataAttributes; + /* Code from the mask modifier in MOD_mask.cc. */ extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, @@ -55,6 +57,149 @@ static bNodeSocketTemplate geo_node_delete_geometry_out[] = { namespace blender::nodes { +template static void copy_data(Span data, MutableSpan r_data, IndexMask mask) +{ + for (const int i_out : mask.index_range()) { + r_data[i_out] = data[mask[i_out]]; + } +} + +static void spline_copy_builtin_attributes(const Spline &spline, + Spline &r_spline, + const IndexMask mask) +{ + copy_data(spline.positions(), r_spline.positions(), mask); + copy_data(spline.radii(), r_spline.radii(), mask); + copy_data(spline.tilts(), r_spline.tilts(), mask); + switch (spline.type()) { + case Spline::Type::Poly: + break; + case Spline::Type::Bezier: { + const BezierSpline &src = static_cast(spline); + BezierSpline &dst = static_cast(r_spline); + copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask); + copy_data(src.handle_positions_right(), dst.handle_positions_right(), mask); + copy_data(src.handle_types_left(), dst.handle_types_left(), mask); + copy_data(src.handle_types_right(), dst.handle_types_right(), mask); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src = static_cast(spline); + NURBSpline &dst = static_cast(r_spline); + copy_data(src.weights(), dst.weights(), mask); + break; + } + } +} + +static void copy_dynamic_attributes(const CustomDataAttributes &src, + CustomDataAttributes &dst, + const IndexMask mask) +{ + src.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional src_attribute = src.get_for_read(name); + BLI_assert(src_attribute); + + if (!dst.create(name, 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); + BLI_assert(new_attribute); + + attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_data(src_attribute->typed(), new_attribute->typed(), mask); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) +{ + SplinePtr new_spline = spline.copy_settings(); + new_spline->resize(mask.size()); + + spline_copy_builtin_attributes(spline, *new_spline, mask); + copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask); + + return new_spline; +} + +static std::unique_ptr curve_delete(const CurveEval &input_curve, + const StringRef name, + const bool invert) +{ + Span input_splines = input_curve.splines(); + std::unique_ptr output_curve = std::make_unique(); + + /* Keep track of which splines were copied to the result to copy spline domain attributes. */ + Vector copied_splines; + + if (input_curve.attributes.get_for_read(name)) { + GVArray_Typed selection = input_curve.attributes.get_for_read(name, false); + for (const int i : input_splines.index_range()) { + if (selection[i] == invert) { + output_curve->add_spline(input_splines[i]->copy()); + copied_splines.append(i); + } + } + } + else { + /* Reuse index vector for each spline. */ + Vector indices_to_copy; + + for (const int i : input_splines.index_range()) { + const Spline &spline = *input_splines[i]; + GVArray_Typed selection = spline.attributes.get_for_read(name, false); + + indices_to_copy.clear(); + for (const int i_point : IndexRange(spline.size())) { + if (selection[i_point] == invert) { + indices_to_copy.append(i_point); + } + } + + /* Avoid creating an empty spline. */ + if (indices_to_copy.is_empty()) { + continue; + } + + SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy)); + output_curve->add_spline(std::move(new_spline)); + copied_splines.append(i); + } + } + + if (copied_splines.is_empty()) { + return {}; + } + + output_curve->attributes.reallocate(output_curve->splines().size()); + copy_dynamic_attributes( + input_curve.attributes, output_curve->attributes, IndexMask(copied_splines)); + + return output_curve; +} + +static void delete_curve_selection(const CurveComponent &in_component, + CurveComponent &r_component, + const StringRef selection_name, + const bool invert) +{ + std::unique_ptr r_curve = curve_delete( + *in_component.get_for_read(), selection_name, invert); + if (r_curve) { + r_component.replace(r_curve.release()); + } + else { + r_component.clear(); + } +} + static void delete_point_cloud_selection(const PointCloudComponent &in_component, PointCloudComponent &out_component, const StringRef selection_name, @@ -509,6 +654,12 @@ static void geo_node_delete_geometry_exec(GeoNodeExecParams params) selection_name, invert); } + if (geometry_set.has()) { + delete_curve_selection(*geometry_set.get_component_for_read(), + out_set.get_component_for_write(), + selection_name, + invert); + } params.set_output("Geometry", std::move(out_set)); } -- cgit v1.2.3 From ddd4b2b785589595927d04eda08c673403994bad Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Thu, 3 Jun 2021 19:12:38 -0400 Subject: Geometry Nodes: Curve Length Node This commit adds a node that outputs the total length of all evalauted curve splines in a geometry set as a float value. Differential Revision: https://developer.blender.org/D11459 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/geometry/nodes/node_geo_curve_length.cc | 58 ++++++++++++++++++++++ 7 files changed, 64 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_curve_length.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index d4c21bf5a23..5e245d81de4 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -504,6 +504,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), NodeItem("GeometryNodeMeshToCurve"), + NodeItem("GeometryNodeCurveLength"), ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ NodeItem("GeometryNodeBoundBox"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 00111910d66..380e75f0924 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1431,6 +1431,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MATERIAL_REPLACE 1051 #define GEO_NODE_MESH_TO_CURVE 1052 #define GEO_NODE_DELETE_GEOMETRY 1053 +#define GEO_NODE_CURVE_LENGTH 1054 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 0923917aa55..2a509fd7df4 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5050,6 +5050,7 @@ static void registerGeometryNodes() register_node_type_geo_boolean(); register_node_type_geo_bounding_box(); register_node_type_geo_collection_info(); + register_node_type_geo_curve_length(); register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_resample(); register_node_type_geo_delete_geometry(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 6ecf46647a0..8baa891df5c 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -162,6 +162,7 @@ set(SRC geometry/nodes/node_geo_bounding_box.cc geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc + geometry/nodes/node_geo_curve_length.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_delete_geometry.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 30b039bca40..3c43b53da5a 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -50,6 +50,7 @@ void register_node_type_geo_attribute_remove(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); +void register_node_type_geo_curve_length(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_resample(void); void register_node_type_geo_delete_geometry(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 081525ffb5b..19b671de49d 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -289,6 +289,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector 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", "") +DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc new file mode 100644 index 00000000000..306085e3b75 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -0,0 +1,58 @@ +/* + * 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_spline.hh" +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_length_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_length_out[] = { + {SOCK_FLOAT, N_("Length")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void geo_node_curve_length_exec(GeoNodeExecParams params) +{ + GeometrySet curve_set = params.extract_input("Curve"); + curve_set = bke::geometry_set_realize_instances(curve_set); + if (!curve_set.has_curve()) { + params.set_output("Length", 0.0f); + return; + } + const CurveEval &curve = *curve_set.get_curve_for_read(); + float length = 0.0f; + for (const SplinePtr &spline : curve.splines()) { + length += spline->length(); + } + params.set_output("Length", length); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_length() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_length_in, geo_node_curve_length_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_length_exec; + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From 053082e9d858370894d121430e07cc43108fe6cc Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 4 Jun 2021 09:14:15 +0200 Subject: Math: Added max_uu/min_uu variations. --- source/blender/blenlib/BLI_math_base.h | 3 +++ source/blender/blenlib/intern/math_base_inline.c | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 028ca31a059..46219ad5493 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -154,6 +154,9 @@ MINLINE int max_iii(int a, int b, int c); MINLINE int min_iiii(int a, int b, int c, int d); MINLINE int max_iiii(int a, int b, int c, int d); +MINLINE uint min_uu(uint a, uint b); +MINLINE uint max_uu(uint a, uint b); + MINLINE size_t min_zz(size_t a, size_t b); MINLINE size_t max_zz(size_t a, size_t b); diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 2a7c091d1b9..d73afff64c8 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -514,6 +514,15 @@ MINLINE int max_ii(int a, int b) return (b < a) ? a : b; } +MINLINE uint min_uu(uint a, uint b) +{ + return (a < b) ? a : b; +} +MINLINE uint max_uu(uint a, uint b) +{ + return (b < a) ? a : b; +} + MINLINE float min_fff(float a, float b, float c) { return min_ff(min_ff(a, b), c); @@ -798,9 +807,9 @@ MINLINE unsigned char unit_float_to_uchar_clamp(float val) MINLINE unsigned short unit_float_to_ushort_clamp(float val) { - return (unsigned short)((val >= 1.0f - 0.5f / 65535) ? - 65535 : - (val <= 0.0f) ? 0 : (val * 65535.0f + 0.5f)); + return (unsigned short)((val >= 1.0f - 0.5f / 65535) ? 65535 : + (val <= 0.0f) ? 0 : + (val * 65535.0f + 0.5f)); } #define unit_float_to_ushort_clamp(val) \ ((CHECK_TYPE_INLINE(val, float)), unit_float_to_ushort_clamp(val)) -- cgit v1.2.3 From f4e0a19d4f1b656c2159d3cdd944d31ebaf9dba5 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 3 Jun 2021 16:45:34 +0200 Subject: Fix T88803: GPencil Thickness modifier produces thicker lines There was a double apply of the thickness due a bug in the fading new parameter. Differential Revision: https://developer.blender.org/D11483 --- source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index 512e3af063a..126949cd659 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -170,8 +170,10 @@ static void deformStroke(GpencilModifierData *md, weight *= curvef; } - float fac_begin = mmd->flag & GP_THICK_NORMALIZE ? 1 : mmd->thickness_fac; - target *= interpf(fac_begin, mmd->fading_end_factor, factor_depth); + /* Apply distance fading. */ + if (mmd->flag & GP_THICK_FADING) { + target = interpf(target, mmd->fading_end_factor, factor_depth); + } pt->pressure = interpf(target, pt->pressure, weight); -- cgit v1.2.3 From 77d7cae2669a6fa37b75a4c474b9c6c6fe3795c3 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 4 Jun 2021 10:44:01 +0200 Subject: GPencil: Cleanup unneeded variable assign The variable is assigned below again and the initial value is not used. --- source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index ea37558fa7f..9f03e493ea8 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -204,8 +204,6 @@ static void deformStroke(GpencilModifierData *md, /* Fill using opacity factor. */ if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) { - gps->fill_opacity_fac = mmd->factor; - float factor_depth = give_opacity_fading_factor(mmd, ob, ob->obmat[3], true); gps->fill_opacity_fac = interpf(mmd->factor, mmd->fading_end_factor, factor_depth); -- cgit v1.2.3 From e4ca6b93ad11188193f9a520db92958aaf0deb75 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 4 Jun 2021 11:40:27 +0200 Subject: BlenLoad: Ensure linked IDs are properly sorted. So far, linked IDs were not properly sorted at all, only the ones explicitely linked from WM code would be, but any indirectly linked data-blocks would end up in some random order in their lists. While not ideal, this is not a huge issue in itself, but it had bad side-effects, e.g. causing (recursive) resync of overrides to happen in random order, leading to mismatches between name indices of newly-generated override IDs and the one existings e.g. And in general, it is much better to be consistent here. Note that the file sub-version is bumped for this commit, since some sorting (the directly linked IDs which we keep a reference to) should never need to be re-done after relevant doversion process. --- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenloader/intern/readfile.c | 7 ++- source/blender/blenloader/intern/versioning_300.c | 71 +++++++++++++++++++---- 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index eb937ac9608..6ad910ff8ab 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 2 +#define BLENDER_FILE_SUBVERSION 3 /* 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/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index fe7d50bfa15..801f5864c8d 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4452,7 +4452,9 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) if (id == NULL) { /* ID has not been read yet, add placeholder to the main of the * library it belongs to, so that it will be read later. */ - read_libblock(fd, libmain, bhead, fd->id_tag_extra | LIB_TAG_INDIRECT, false, NULL); + read_libblock(fd, libmain, bhead, fd->id_tag_extra | LIB_TAG_INDIRECT, false, &id); + id_sort_by_name(which_libbase(libmain, GS(id->name)), id, id->prev); + /* commented because this can print way too much */ // if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->filepath); @@ -4512,7 +4514,8 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) bhead, fd->id_tag_extra | LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, false, - NULL); + &id); + id_sort_by_name(which_libbase(mainvar, GS(id->name)), id, id->prev); } else { /* Convert any previously read weak link to regular link diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index c175714c537..284a30a280d 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -27,6 +27,7 @@ #include "DNA_brush_types.h" #include "DNA_genfile.h" +#include "DNA_listBase.h" #include "DNA_modifier_types.h" #include "DNA_text_types.h" @@ -37,6 +38,43 @@ #include "BLO_readfile.h" #include "readfile.h" +static void sort_linked_ids(Main *bmain) +{ + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + ListBase temp_list; + BLI_listbase_clear(&temp_list); + LISTBASE_FOREACH_MUTABLE (ID *, id, lb) { + if (ID_IS_LINKED(id)) { + BLI_remlink(lb, id); + BLI_addtail(&temp_list, id); + id_sort_by_name(&temp_list, id, NULL); + } + } + BLI_movelisttolist(lb, &temp_list); + } + FOREACH_MAIN_LISTBASE_END; +} + +static void assert_sorted_ids(Main *bmain) +{ +#ifndef NDEBUG + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + ID *id_prev = NULL; + LISTBASE_FOREACH (ID *, id, lb) { + if (id_prev == NULL) { + continue; + } + BLI_assert(id_prev->lib != id->lib || BLI_strcasecmp(id_prev->name, id->name) < 0); + } + } + FOREACH_MAIN_LISTBASE_END; +#else + UNUSED_VARS_NDEBUG(bmain); +#endif +} + void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) { if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) { @@ -47,19 +85,8 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #blo_do_versions_300 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 3)) { /* Use new texture socket in Attribute Sample Texture node. */ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type != NTREE_GEOMETRY) { @@ -83,6 +110,26 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) node->id = NULL; } } + + sort_linked_ids(bmain); + assert_sorted_ids(bmain); + } + if (MAIN_VERSION_ATLEAST(bmain, 300, 3)) { + assert_sorted_ids(bmain); + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #blo_do_versions_300 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } -- cgit v1.2.3 From 9ba6b64efa3acb941dee58207489bc593449f133 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 4 Jun 2021 12:17:02 +0200 Subject: Greasepencil: show pressure curve widgets in the sidebar These were only showing in the Properties Editor, but there is no reason to have the panels be different in the sidebar (they should not show in the top bar though). agreed upon by both @anoniov and @mendio ref T88787 --- release/scripts/startup/bl_ui/properties_paint_common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index f3462dfb35d..4bfd2fd32b0 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1235,7 +1235,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row.prop(brush, "size", text="Radius") row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') - if gp_settings.use_pressure and context.area.type == 'PROPERTIES': + if gp_settings.use_pressure and not compact: col = layout.column() col.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True, use_negative_slope=True) @@ -1244,7 +1244,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row.prop(gp_settings, "pen_strength", slider=True) row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') - if gp_settings.use_strength_pressure and context.area.type == 'PROPERTIES': + if gp_settings.use_strength_pressure and not compact: col = layout.column() col.template_curve_mapping(gp_settings, "curve_strength", brush=True, use_negative_slope=True) -- cgit v1.2.3 From d486ee2dbdd34e00a4d575fab6feb3e36b486275 Mon Sep 17 00:00:00 2001 From: Fynn Grotehans Date: Fri, 4 Jun 2021 11:50:23 +0200 Subject: Update Camera presets The (tracking) camera presets have not been updated in the last 7 or more years, so they are very outdated. I found it pointless to have a few specific camera models in the list and instead add the most commonly used sensor sizes/film sizes. This way the list is shorter, easier to maintain/becomes later outdated, and is more user friendly for most people who don't own any of the specific cameras. I added the Crop Factor to the Beginning of the name, so it gets sortet in the correct order and presets are easier to find based on the size. Reviewed By: #render_cycles, #motion_tracking, brecht, sergey Differential Revision: https://developer.blender.org/D10739 --- release/scripts/modules/bpy/path.py | 1 + release/scripts/presets/camera/1_colon_2.3_inch.py | 4 ---- release/scripts/presets/camera/1_colon_2.5_inch.py | 4 ---- release/scripts/presets/camera/1_inch.py | 4 ++++ release/scripts/presets/camera/1_slash_1.8_inch.py | 4 ++++ release/scripts/presets/camera/1_slash_2.3_inch.py | 4 ++++ release/scripts/presets/camera/1_slash_2.5_inch.py | 4 ++++ release/scripts/presets/camera/1_slash_2.7_inch.py | 4 ++++ release/scripts/presets/camera/1_slash_3.2_inch.py | 4 ++++ release/scripts/presets/camera/2_colon_3_inch.py | 4 ---- release/scripts/presets/camera/2_slash_3_inch.py | 4 ++++ release/scripts/presets/camera/4_colon_3_inch.py | 4 ---- release/scripts/presets/camera/APS-C.py | 4 ++++ release/scripts/presets/camera/APS-C_(Canon).py | 4 ++++ release/scripts/presets/camera/APS-H_(Canon).py | 4 ++++ release/scripts/presets/camera/Analog_16mm.py | 4 ++++ release/scripts/presets/camera/Analog_35mm.py | 4 ++++ release/scripts/presets/camera/Analog_65mm.py | 4 ++++ release/scripts/presets/camera/Analog_IMAX.py | 4 ++++ release/scripts/presets/camera/Analog_Super_16.py | 4 ++++ release/scripts/presets/camera/Analog_Super_35.py | 4 ++++ release/scripts/presets/camera/Arri_Alexa.py | 4 ---- release/scripts/presets/camera/Arri_Alexa_65.py | 4 ++++ release/scripts/presets/camera/Arri_Alexa_LF.py | 4 ++++ release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py | 4 ++++ release/scripts/presets/camera/Blackmagic_Cinema_Camera.py | 4 ---- release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py | 4 ++++ release/scripts/presets/camera/Blackmagic_Pocket_4K.py | 4 ++++ release/scripts/presets/camera/Blackmagic_Pocket_6k.py | 4 ++++ .../scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py | 4 ---- .../scripts/presets/camera/Blackmagic_Production_Camera_4K.py | 4 ---- release/scripts/presets/camera/Blackmagic_URSA_4.6K.py | 4 ++++ release/scripts/presets/camera/Blender.py | 4 ---- release/scripts/presets/camera/Canon_1100D.py | 4 ---- release/scripts/presets/camera/Canon_APS-C.py | 4 ---- release/scripts/presets/camera/Canon_APS-H.py | 4 ---- release/scripts/presets/camera/Canon_C300.py | 4 ---- release/scripts/presets/camera/Foveon_(Sigma).py | 4 ++++ release/scripts/presets/camera/Full_Frame_35mm_Camera.py | 4 ---- release/scripts/presets/camera/Fullframe.py | 4 ++++ release/scripts/presets/camera/GoPro_Hero3_Black.py | 6 ------ release/scripts/presets/camera/GoPro_Hero3_Silver.py | 6 ------ release/scripts/presets/camera/GoPro_Hero3_White.py | 6 ------ release/scripts/presets/camera/MFT.py | 4 ++++ release/scripts/presets/camera/Medium-format_(Hasselblad).py | 4 ++++ release/scripts/presets/camera/Nexus_5.py | 5 ----- release/scripts/presets/camera/Nikon_D3100.py | 4 ---- release/scripts/presets/camera/Nikon_DX.py | 4 ---- release/scripts/presets/camera/Panasonic_AG-HVX200.py | 4 ---- release/scripts/presets/camera/Panasonic_LX2.py | 4 ---- release/scripts/presets/camera/RED_Dragon_5K.py | 4 ++++ release/scripts/presets/camera/RED_Dragon_6K.py | 4 ++++ release/scripts/presets/camera/RED_Helium_8K.py | 4 ++++ release/scripts/presets/camera/RED_Monstro_8K.py | 4 ++++ release/scripts/presets/camera/Red_Epic.py | 4 ---- release/scripts/presets/camera/Red_One_2K.py | 4 ---- release/scripts/presets/camera/Red_One_3K.py | 4 ---- release/scripts/presets/camera/Red_One_4K.py | 4 ---- release/scripts/presets/camera/Samsung_Galaxy_S3.py | 5 ----- release/scripts/presets/camera/Samsung_Galaxy_S4.py | 5 ----- release/scripts/presets/camera/Sony_A55.py | 4 ---- release/scripts/presets/camera/Sony_EX1.py | 4 ---- release/scripts/presets/camera/Sony_F65.py | 4 ---- release/scripts/presets/camera/Super_16_Film.py | 4 ---- release/scripts/presets/camera/Super_35_Film.py | 4 ---- release/scripts/presets/camera/iPhone_4.py | 5 ----- release/scripts/presets/camera/iPhone_4S.py | 5 ----- release/scripts/presets/camera/iPhone_5.py | 5 ----- release/scripts/presets/tracking_camera/1__colon__2.3_inch.py | 9 --------- release/scripts/presets/tracking_camera/1__colon__2.5_inch.py | 9 --------- release/scripts/presets/tracking_camera/1_inch.py | 4 ++++ release/scripts/presets/tracking_camera/1_slash_1.8_inch.py | 4 ++++ release/scripts/presets/tracking_camera/1_slash_2.3_inch.py | 4 ++++ release/scripts/presets/tracking_camera/1_slash_2.5_inch.py | 4 ++++ release/scripts/presets/tracking_camera/1_slash_2.7_inch.py | 4 ++++ release/scripts/presets/tracking_camera/1_slash_3.2_inch.py | 4 ++++ release/scripts/presets/tracking_camera/2__colon__3_inch.py | 9 --------- release/scripts/presets/tracking_camera/2_slash_3_inch.py | 4 ++++ release/scripts/presets/tracking_camera/4__colon__3_inch.py | 9 --------- release/scripts/presets/tracking_camera/APS-C.py | 4 ++++ release/scripts/presets/tracking_camera/APS-C_(Canon).py | 4 ++++ release/scripts/presets/tracking_camera/APS-H_(Canon).py | 4 ++++ release/scripts/presets/tracking_camera/Analog_16mm.py | 4 ++++ release/scripts/presets/tracking_camera/Analog_35mm.py | 4 ++++ release/scripts/presets/tracking_camera/Analog_65mm.py | 4 ++++ release/scripts/presets/tracking_camera/Analog_IMAX.py | 4 ++++ release/scripts/presets/tracking_camera/Analog_Super_16.py | 4 ++++ release/scripts/presets/tracking_camera/Analog_Super_35.py | 4 ++++ release/scripts/presets/tracking_camera/Arri_Alexa.py | 9 --------- release/scripts/presets/tracking_camera/Arri_Alexa_65.py | 4 ++++ release/scripts/presets/tracking_camera/Arri_Alexa_LF.py | 4 ++++ .../scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py | 4 ++++ .../presets/tracking_camera/Blackmagic_Cinema_Camera.py | 9 --------- .../presets/tracking_camera/Blackmagic_Pocket_&_Studio.py | 4 ++++ .../scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py | 4 ++++ .../scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py | 4 ++++ .../presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py | 9 --------- .../presets/tracking_camera/Blackmagic_Production_Camera_4K.py | 9 --------- .../scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py | 4 ++++ release/scripts/presets/tracking_camera/Blender.py | 10 ---------- release/scripts/presets/tracking_camera/Canon_1100D.py | 9 --------- release/scripts/presets/tracking_camera/Canon_APS-C.py | 9 --------- release/scripts/presets/tracking_camera/Canon_APS-H.py | 9 --------- release/scripts/presets/tracking_camera/Canon_C300.py | 9 --------- release/scripts/presets/tracking_camera/Foveon_(Sigma).py | 4 ++++ .../scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py | 9 --------- release/scripts/presets/tracking_camera/Fullframe.py | 4 ++++ release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py | 10 ---------- release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py | 10 ---------- release/scripts/presets/tracking_camera/GoPro_Hero3_White.py | 10 ---------- release/scripts/presets/tracking_camera/MFT.py | 4 ++++ .../presets/tracking_camera/Medium-format_(Hasselblad).py | 4 ++++ release/scripts/presets/tracking_camera/Nexus_5.py | 10 ---------- release/scripts/presets/tracking_camera/Nikon_D3100.py | 9 --------- release/scripts/presets/tracking_camera/Nikon_DX.py | 9 --------- release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py | 9 --------- release/scripts/presets/tracking_camera/Panasonic_LX2.py | 9 --------- release/scripts/presets/tracking_camera/RED_Dragon_5K.py | 4 ++++ release/scripts/presets/tracking_camera/RED_Dragon_6K.py | 4 ++++ release/scripts/presets/tracking_camera/RED_Helium_8K.py | 4 ++++ release/scripts/presets/tracking_camera/RED_Monstro_8K.py | 4 ++++ release/scripts/presets/tracking_camera/Red_Epic.py | 9 --------- release/scripts/presets/tracking_camera/Red_One_2K.py | 9 --------- release/scripts/presets/tracking_camera/Red_One_3K.py | 9 --------- release/scripts/presets/tracking_camera/Red_One_4K.py | 9 --------- release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py | 10 ---------- release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py | 10 ---------- release/scripts/presets/tracking_camera/Sony_A55.py | 9 --------- release/scripts/presets/tracking_camera/Sony_EX1.py | 9 --------- release/scripts/presets/tracking_camera/Sony_F65.py | 9 --------- release/scripts/presets/tracking_camera/Super_16.py | 9 --------- release/scripts/presets/tracking_camera/Super_35.py | 9 --------- release/scripts/presets/tracking_camera/iPhone_4.py | 10 ---------- release/scripts/presets/tracking_camera/iPhone_4S.py | 10 ---------- release/scripts/presets/tracking_camera/iPhone_5.py | 10 ---------- 135 files changed, 249 insertions(+), 490 deletions(-) delete mode 100644 release/scripts/presets/camera/1_colon_2.3_inch.py delete mode 100644 release/scripts/presets/camera/1_colon_2.5_inch.py create mode 100644 release/scripts/presets/camera/1_inch.py create mode 100644 release/scripts/presets/camera/1_slash_1.8_inch.py create mode 100644 release/scripts/presets/camera/1_slash_2.3_inch.py create mode 100644 release/scripts/presets/camera/1_slash_2.5_inch.py create mode 100644 release/scripts/presets/camera/1_slash_2.7_inch.py create mode 100644 release/scripts/presets/camera/1_slash_3.2_inch.py delete mode 100644 release/scripts/presets/camera/2_colon_3_inch.py create mode 100644 release/scripts/presets/camera/2_slash_3_inch.py delete mode 100644 release/scripts/presets/camera/4_colon_3_inch.py create mode 100644 release/scripts/presets/camera/APS-C.py create mode 100644 release/scripts/presets/camera/APS-C_(Canon).py create mode 100644 release/scripts/presets/camera/APS-H_(Canon).py create mode 100644 release/scripts/presets/camera/Analog_16mm.py create mode 100644 release/scripts/presets/camera/Analog_35mm.py create mode 100644 release/scripts/presets/camera/Analog_65mm.py create mode 100644 release/scripts/presets/camera/Analog_IMAX.py create mode 100644 release/scripts/presets/camera/Analog_Super_16.py create mode 100644 release/scripts/presets/camera/Analog_Super_35.py delete mode 100644 release/scripts/presets/camera/Arri_Alexa.py create mode 100644 release/scripts/presets/camera/Arri_Alexa_65.py create mode 100644 release/scripts/presets/camera/Arri_Alexa_LF.py create mode 100644 release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py delete mode 100644 release/scripts/presets/camera/Blackmagic_Cinema_Camera.py create mode 100644 release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py create mode 100644 release/scripts/presets/camera/Blackmagic_Pocket_4K.py create mode 100644 release/scripts/presets/camera/Blackmagic_Pocket_6k.py delete mode 100644 release/scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py delete mode 100644 release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py create mode 100644 release/scripts/presets/camera/Blackmagic_URSA_4.6K.py delete mode 100644 release/scripts/presets/camera/Blender.py delete mode 100644 release/scripts/presets/camera/Canon_1100D.py delete mode 100644 release/scripts/presets/camera/Canon_APS-C.py delete mode 100644 release/scripts/presets/camera/Canon_APS-H.py delete mode 100644 release/scripts/presets/camera/Canon_C300.py create mode 100644 release/scripts/presets/camera/Foveon_(Sigma).py delete mode 100644 release/scripts/presets/camera/Full_Frame_35mm_Camera.py create mode 100644 release/scripts/presets/camera/Fullframe.py delete mode 100644 release/scripts/presets/camera/GoPro_Hero3_Black.py delete mode 100644 release/scripts/presets/camera/GoPro_Hero3_Silver.py delete mode 100644 release/scripts/presets/camera/GoPro_Hero3_White.py create mode 100644 release/scripts/presets/camera/MFT.py create mode 100644 release/scripts/presets/camera/Medium-format_(Hasselblad).py delete mode 100644 release/scripts/presets/camera/Nexus_5.py delete mode 100644 release/scripts/presets/camera/Nikon_D3100.py delete mode 100644 release/scripts/presets/camera/Nikon_DX.py delete mode 100644 release/scripts/presets/camera/Panasonic_AG-HVX200.py delete mode 100644 release/scripts/presets/camera/Panasonic_LX2.py create mode 100644 release/scripts/presets/camera/RED_Dragon_5K.py create mode 100644 release/scripts/presets/camera/RED_Dragon_6K.py create mode 100644 release/scripts/presets/camera/RED_Helium_8K.py create mode 100644 release/scripts/presets/camera/RED_Monstro_8K.py delete mode 100644 release/scripts/presets/camera/Red_Epic.py delete mode 100644 release/scripts/presets/camera/Red_One_2K.py delete mode 100644 release/scripts/presets/camera/Red_One_3K.py delete mode 100644 release/scripts/presets/camera/Red_One_4K.py delete mode 100644 release/scripts/presets/camera/Samsung_Galaxy_S3.py delete mode 100644 release/scripts/presets/camera/Samsung_Galaxy_S4.py delete mode 100644 release/scripts/presets/camera/Sony_A55.py delete mode 100644 release/scripts/presets/camera/Sony_EX1.py delete mode 100644 release/scripts/presets/camera/Sony_F65.py delete mode 100644 release/scripts/presets/camera/Super_16_Film.py delete mode 100644 release/scripts/presets/camera/Super_35_Film.py delete mode 100644 release/scripts/presets/camera/iPhone_4.py delete mode 100644 release/scripts/presets/camera/iPhone_4S.py delete mode 100644 release/scripts/presets/camera/iPhone_5.py delete mode 100644 release/scripts/presets/tracking_camera/1__colon__2.3_inch.py delete mode 100644 release/scripts/presets/tracking_camera/1__colon__2.5_inch.py create mode 100644 release/scripts/presets/tracking_camera/1_inch.py create mode 100644 release/scripts/presets/tracking_camera/1_slash_1.8_inch.py create mode 100644 release/scripts/presets/tracking_camera/1_slash_2.3_inch.py create mode 100644 release/scripts/presets/tracking_camera/1_slash_2.5_inch.py create mode 100644 release/scripts/presets/tracking_camera/1_slash_2.7_inch.py create mode 100644 release/scripts/presets/tracking_camera/1_slash_3.2_inch.py delete mode 100644 release/scripts/presets/tracking_camera/2__colon__3_inch.py create mode 100644 release/scripts/presets/tracking_camera/2_slash_3_inch.py delete mode 100644 release/scripts/presets/tracking_camera/4__colon__3_inch.py create mode 100644 release/scripts/presets/tracking_camera/APS-C.py create mode 100644 release/scripts/presets/tracking_camera/APS-C_(Canon).py create mode 100644 release/scripts/presets/tracking_camera/APS-H_(Canon).py create mode 100644 release/scripts/presets/tracking_camera/Analog_16mm.py create mode 100644 release/scripts/presets/tracking_camera/Analog_35mm.py create mode 100644 release/scripts/presets/tracking_camera/Analog_65mm.py create mode 100644 release/scripts/presets/tracking_camera/Analog_IMAX.py create mode 100644 release/scripts/presets/tracking_camera/Analog_Super_16.py create mode 100644 release/scripts/presets/tracking_camera/Analog_Super_35.py delete mode 100644 release/scripts/presets/tracking_camera/Arri_Alexa.py create mode 100644 release/scripts/presets/tracking_camera/Arri_Alexa_65.py create mode 100644 release/scripts/presets/tracking_camera/Arri_Alexa_LF.py create mode 100644 release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py delete mode 100644 release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py create mode 100644 release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py create mode 100644 release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py create mode 100644 release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py delete mode 100644 release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py delete mode 100644 release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py create mode 100644 release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py delete mode 100644 release/scripts/presets/tracking_camera/Blender.py delete mode 100644 release/scripts/presets/tracking_camera/Canon_1100D.py delete mode 100644 release/scripts/presets/tracking_camera/Canon_APS-C.py delete mode 100644 release/scripts/presets/tracking_camera/Canon_APS-H.py delete mode 100644 release/scripts/presets/tracking_camera/Canon_C300.py create mode 100644 release/scripts/presets/tracking_camera/Foveon_(Sigma).py delete mode 100644 release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py create mode 100644 release/scripts/presets/tracking_camera/Fullframe.py delete mode 100644 release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py delete mode 100644 release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py delete mode 100644 release/scripts/presets/tracking_camera/GoPro_Hero3_White.py create mode 100644 release/scripts/presets/tracking_camera/MFT.py create mode 100644 release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py delete mode 100644 release/scripts/presets/tracking_camera/Nexus_5.py delete mode 100644 release/scripts/presets/tracking_camera/Nikon_D3100.py delete mode 100644 release/scripts/presets/tracking_camera/Nikon_DX.py delete mode 100644 release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py delete mode 100644 release/scripts/presets/tracking_camera/Panasonic_LX2.py create mode 100644 release/scripts/presets/tracking_camera/RED_Dragon_5K.py create mode 100644 release/scripts/presets/tracking_camera/RED_Dragon_6K.py create mode 100644 release/scripts/presets/tracking_camera/RED_Helium_8K.py create mode 100644 release/scripts/presets/tracking_camera/RED_Monstro_8K.py delete mode 100644 release/scripts/presets/tracking_camera/Red_Epic.py delete mode 100644 release/scripts/presets/tracking_camera/Red_One_2K.py delete mode 100644 release/scripts/presets/tracking_camera/Red_One_3K.py delete mode 100644 release/scripts/presets/tracking_camera/Red_One_4K.py delete mode 100644 release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py delete mode 100644 release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py delete mode 100644 release/scripts/presets/tracking_camera/Sony_A55.py delete mode 100644 release/scripts/presets/tracking_camera/Sony_EX1.py delete mode 100644 release/scripts/presets/tracking_camera/Sony_F65.py delete mode 100644 release/scripts/presets/tracking_camera/Super_16.py delete mode 100644 release/scripts/presets/tracking_camera/Super_35.py delete mode 100644 release/scripts/presets/tracking_camera/iPhone_4.py delete mode 100644 release/scripts/presets/tracking_camera/iPhone_4S.py delete mode 100644 release/scripts/presets/tracking_camera/iPhone_5.py diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py index e9e9671cc35..1de4542e69e 100644 --- a/release/scripts/modules/bpy/path.py +++ b/release/scripts/modules/bpy/path.py @@ -198,6 +198,7 @@ def _clean_utf8(name): _display_name_literals = { ":": "_colon_", "+": "_plus_", + "/": "_slash_", } diff --git a/release/scripts/presets/camera/1_colon_2.3_inch.py b/release/scripts/presets/camera/1_colon_2.3_inch.py deleted file mode 100644 index 72548384401..00000000000 --- a/release/scripts/presets/camera/1_colon_2.3_inch.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 6.16 -bpy.context.camera.sensor_height = 4.62 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/1_colon_2.5_inch.py b/release/scripts/presets/camera/1_colon_2.5_inch.py deleted file mode 100644 index 90f60e7d7f0..00000000000 --- a/release/scripts/presets/camera/1_colon_2.5_inch.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 5.76 -bpy.context.camera.sensor_height = 4.29 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/1_inch.py b/release/scripts/presets/camera/1_inch.py new file mode 100644 index 00000000000..72b039fb978 --- /dev/null +++ b/release/scripts/presets/camera/1_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 13.2 +bpy.context.camera.sensor_height = 8.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/1_slash_1.8_inch.py b/release/scripts/presets/camera/1_slash_1.8_inch.py new file mode 100644 index 00000000000..38e09182de6 --- /dev/null +++ b/release/scripts/presets/camera/1_slash_1.8_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 7.18 +bpy.context.camera.sensor_height = 5.32 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/1_slash_2.3_inch.py b/release/scripts/presets/camera/1_slash_2.3_inch.py new file mode 100644 index 00000000000..4d55738f4ed --- /dev/null +++ b/release/scripts/presets/camera/1_slash_2.3_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 6.17 +bpy.context.camera.sensor_height = 4.55 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/1_slash_2.5_inch.py b/release/scripts/presets/camera/1_slash_2.5_inch.py new file mode 100644 index 00000000000..cbdb6f3cbe0 --- /dev/null +++ b/release/scripts/presets/camera/1_slash_2.5_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 5.76 +bpy.context.camera.sensor_height = 4.29 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/1_slash_2.7_inch.py b/release/scripts/presets/camera/1_slash_2.7_inch.py new file mode 100644 index 00000000000..5ccfa4ab555 --- /dev/null +++ b/release/scripts/presets/camera/1_slash_2.7_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 5.37 +bpy.context.camera.sensor_height = 4.04 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/1_slash_3.2_inch.py b/release/scripts/presets/camera/1_slash_3.2_inch.py new file mode 100644 index 00000000000..1963f7ec048 --- /dev/null +++ b/release/scripts/presets/camera/1_slash_3.2_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 4.54 +bpy.context.camera.sensor_height = 3.42 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/2_colon_3_inch.py b/release/scripts/presets/camera/2_colon_3_inch.py deleted file mode 100644 index 46436970efc..00000000000 --- a/release/scripts/presets/camera/2_colon_3_inch.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 9.6 -bpy.context.camera.sensor_height = 5.4 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/2_slash_3_inch.py b/release/scripts/presets/camera/2_slash_3_inch.py new file mode 100644 index 00000000000..25b46016800 --- /dev/null +++ b/release/scripts/presets/camera/2_slash_3_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 8.8 +bpy.context.camera.sensor_height = 6.6 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/4_colon_3_inch.py b/release/scripts/presets/camera/4_colon_3_inch.py deleted file mode 100644 index 88346c01ef8..00000000000 --- a/release/scripts/presets/camera/4_colon_3_inch.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 17.31 -bpy.context.camera.sensor_height = 12.98 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/APS-C.py b/release/scripts/presets/camera/APS-C.py new file mode 100644 index 00000000000..84e40825248 --- /dev/null +++ b/release/scripts/presets/camera/APS-C.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 23.6 +bpy.context.camera.sensor_height = 15.6 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/APS-C_(Canon).py b/release/scripts/presets/camera/APS-C_(Canon).py new file mode 100644 index 00000000000..55f20ce0eac --- /dev/null +++ b/release/scripts/presets/camera/APS-C_(Canon).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 22.30 +bpy.context.camera.sensor_height = 14.90 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/APS-H_(Canon).py b/release/scripts/presets/camera/APS-H_(Canon).py new file mode 100644 index 00000000000..d63f733280b --- /dev/null +++ b/release/scripts/presets/camera/APS-H_(Canon).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 27.90 +bpy.context.camera.sensor_height = 18.60 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_16mm.py b/release/scripts/presets/camera/Analog_16mm.py new file mode 100644 index 00000000000..aa98eaf2408 --- /dev/null +++ b/release/scripts/presets/camera/Analog_16mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 10.26 +bpy.context.camera.sensor_height = 7.49 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_35mm.py b/release/scripts/presets/camera/Analog_35mm.py new file mode 100644 index 00000000000..a0dee1f0166 --- /dev/null +++ b/release/scripts/presets/camera/Analog_35mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 22 +bpy.context.camera.sensor_height = 16 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_65mm.py b/release/scripts/presets/camera/Analog_65mm.py new file mode 100644 index 00000000000..8de91ac0ee3 --- /dev/null +++ b/release/scripts/presets/camera/Analog_65mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 52.45 +bpy.context.camera.sensor_height = 23.01 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_IMAX.py b/release/scripts/presets/camera/Analog_IMAX.py new file mode 100644 index 00000000000..5a445f3de8c --- /dev/null +++ b/release/scripts/presets/camera/Analog_IMAX.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 71.41 +bpy.context.camera.sensor_height = 52.63 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_Super_16.py b/release/scripts/presets/camera/Analog_Super_16.py new file mode 100644 index 00000000000..a340a31dc25 --- /dev/null +++ b/release/scripts/presets/camera/Analog_Super_16.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 12.35 +bpy.context.camera.sensor_height = 7.42 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Analog_Super_35.py b/release/scripts/presets/camera/Analog_Super_35.py new file mode 100644 index 00000000000..3c8f1837253 --- /dev/null +++ b/release/scripts/presets/camera/Analog_Super_35.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 24.89 +bpy.context.camera.sensor_height = 18.66 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Arri_Alexa.py b/release/scripts/presets/camera/Arri_Alexa.py deleted file mode 100644 index 6a6cdfee12b..00000000000 --- a/release/scripts/presets/camera/Arri_Alexa.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 23.760 -bpy.context.camera.sensor_height = 13.365 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Arri_Alexa_65.py b/release/scripts/presets/camera/Arri_Alexa_65.py new file mode 100644 index 00000000000..b1467709949 --- /dev/null +++ b/release/scripts/presets/camera/Arri_Alexa_65.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 54.12 +bpy.context.camera.sensor_height = 25.58 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Arri_Alexa_LF.py b/release/scripts/presets/camera/Arri_Alexa_LF.py new file mode 100644 index 00000000000..1cde94fce8d --- /dev/null +++ b/release/scripts/presets/camera/Arri_Alexa_LF.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 36.70 +bpy.context.camera.sensor_height = 25.54 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py b/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py new file mode 100644 index 00000000000..0f61d35a0f9 --- /dev/null +++ b/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 29.90 +bpy.context.camera.sensor_height = 15.77 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Blackmagic_Cinema_Camera.py b/release/scripts/presets/camera/Blackmagic_Cinema_Camera.py deleted file mode 100644 index 6fde30756da..00000000000 --- a/release/scripts/presets/camera/Blackmagic_Cinema_Camera.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 15.81 -bpy.context.camera.sensor_height = 8.88 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py b/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py new file mode 100644 index 00000000000..260bfbaf94f --- /dev/null +++ b/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 12.48 +bpy.context.camera.sensor_height = 7.02 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_4K.py b/release/scripts/presets/camera/Blackmagic_Pocket_4K.py new file mode 100644 index 00000000000..dc057397828 --- /dev/null +++ b/release/scripts/presets/camera/Blackmagic_Pocket_4K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 18.96 +bpy.context.camera.sensor_height = 10.00 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_6k.py b/release/scripts/presets/camera/Blackmagic_Pocket_6k.py new file mode 100644 index 00000000000..a483f3d5f98 --- /dev/null +++ b/release/scripts/presets/camera/Blackmagic_Pocket_6k.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 23.10 +bpy.context.camera.sensor_height = 12.99 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py b/release/scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py deleted file mode 100644 index bb2b172919e..00000000000 --- a/release/scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 12.48 -bpy.context.camera.sensor_height = 7.02 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py b/release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py deleted file mode 100644 index dbc12c5aa68..00000000000 --- a/release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 21.12 -bpy.context.camera.sensor_height = 11.88 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py b/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py new file mode 100644 index 00000000000..c71e42d72d3 --- /dev/null +++ b/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 25.34 +bpy.context.camera.sensor_height = 14.25 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Blender.py b/release/scripts/presets/camera/Blender.py deleted file mode 100644 index ca4906fbb39..00000000000 --- a/release/scripts/presets/camera/Blender.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 32 -bpy.context.camera.sensor_height = 18 -bpy.context.camera.sensor_fit = 'AUTO' diff --git a/release/scripts/presets/camera/Canon_1100D.py b/release/scripts/presets/camera/Canon_1100D.py deleted file mode 100644 index e665e9e95d5..00000000000 --- a/release/scripts/presets/camera/Canon_1100D.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 22.2 -bpy.context.camera.sensor_height = 14.7 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Canon_APS-C.py b/release/scripts/presets/camera/Canon_APS-C.py deleted file mode 100644 index 95108b2187f..00000000000 --- a/release/scripts/presets/camera/Canon_APS-C.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 22.3 -bpy.context.camera.sensor_height = 14.9 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Canon_APS-H.py b/release/scripts/presets/camera/Canon_APS-H.py deleted file mode 100644 index d3b61d1aa46..00000000000 --- a/release/scripts/presets/camera/Canon_APS-H.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 27.90 -bpy.context.camera.sensor_height = 18.60 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Canon_C300.py b/release/scripts/presets/camera/Canon_C300.py deleted file mode 100644 index e22af779854..00000000000 --- a/release/scripts/presets/camera/Canon_C300.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 24.4 -bpy.context.camera.sensor_height = 13.5 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Foveon_(Sigma).py b/release/scripts/presets/camera/Foveon_(Sigma).py new file mode 100644 index 00000000000..e6a1a0ed344 --- /dev/null +++ b/release/scripts/presets/camera/Foveon_(Sigma).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 20.70 +bpy.context.camera.sensor_height = 13.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Full_Frame_35mm_Camera.py b/release/scripts/presets/camera/Full_Frame_35mm_Camera.py deleted file mode 100644 index c8017331b28..00000000000 --- a/release/scripts/presets/camera/Full_Frame_35mm_Camera.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 36 -bpy.context.camera.sensor_height = 24 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Fullframe.py b/release/scripts/presets/camera/Fullframe.py new file mode 100644 index 00000000000..95fb4afc10b --- /dev/null +++ b/release/scripts/presets/camera/Fullframe.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 36 +bpy.context.camera.sensor_height = 24 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/GoPro_Hero3_Black.py b/release/scripts/presets/camera/GoPro_Hero3_Black.py deleted file mode 100644 index e294f802a02..00000000000 --- a/release/scripts/presets/camera/GoPro_Hero3_Black.py +++ /dev/null @@ -1,6 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 6.16 -bpy.context.camera.sensor_height = 4.62 -bpy.context.camera.lens = 2.77 - -bpy.context.camera.sensor_fit = 'AUTO' diff --git a/release/scripts/presets/camera/GoPro_Hero3_Silver.py b/release/scripts/presets/camera/GoPro_Hero3_Silver.py deleted file mode 100644 index 247bd7c4aaf..00000000000 --- a/release/scripts/presets/camera/GoPro_Hero3_Silver.py +++ /dev/null @@ -1,6 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 5.371 -bpy.context.camera.sensor_height = 4.035 -bpy.context.camera.lens = 2.77 - -bpy.context.camera.sensor_fit = 'AUTO' diff --git a/release/scripts/presets/camera/GoPro_Hero3_White.py b/release/scripts/presets/camera/GoPro_Hero3_White.py deleted file mode 100644 index 948f838f5d6..00000000000 --- a/release/scripts/presets/camera/GoPro_Hero3_White.py +++ /dev/null @@ -1,6 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 5.76 -bpy.context.camera.sensor_height = 4.29 -bpy.context.camera.lens = 2.77 - -bpy.context.camera.sensor_fit = 'AUTO' diff --git a/release/scripts/presets/camera/MFT.py b/release/scripts/presets/camera/MFT.py new file mode 100644 index 00000000000..bc0dd49baa8 --- /dev/null +++ b/release/scripts/presets/camera/MFT.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 17.3 +bpy.context.camera.sensor_height = 13.0 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Medium-format_(Hasselblad).py b/release/scripts/presets/camera/Medium-format_(Hasselblad).py new file mode 100644 index 00000000000..e9b16024b79 --- /dev/null +++ b/release/scripts/presets/camera/Medium-format_(Hasselblad).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 44 +bpy.context.camera.sensor_height = 33 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Nexus_5.py b/release/scripts/presets/camera/Nexus_5.py deleted file mode 100644 index 36e741cbba5..00000000000 --- a/release/scripts/presets/camera/Nexus_5.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.5 -bpy.context.camera.sensor_height = 3.37 -bpy.context.camera.lens = 3.91 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Nikon_D3100.py b/release/scripts/presets/camera/Nikon_D3100.py deleted file mode 100644 index b4ceb3aa721..00000000000 --- a/release/scripts/presets/camera/Nikon_D3100.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 23.1 -bpy.context.camera.sensor_height = 15.4 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Nikon_DX.py b/release/scripts/presets/camera/Nikon_DX.py deleted file mode 100644 index dbe9e7fcc18..00000000000 --- a/release/scripts/presets/camera/Nikon_DX.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 23.6 -bpy.context.camera.sensor_height = 15.8 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Panasonic_AG-HVX200.py b/release/scripts/presets/camera/Panasonic_AG-HVX200.py deleted file mode 100644 index 71ad3c3a161..00000000000 --- a/release/scripts/presets/camera/Panasonic_AG-HVX200.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.68 -bpy.context.camera.sensor_height = 2.633 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Panasonic_LX2.py b/release/scripts/presets/camera/Panasonic_LX2.py deleted file mode 100644 index d66e02e32d4..00000000000 --- a/release/scripts/presets/camera/Panasonic_LX2.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 8.5 -bpy.context.camera.sensor_height = 4.78 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/RED_Dragon_5K.py b/release/scripts/presets/camera/RED_Dragon_5K.py new file mode 100644 index 00000000000..fa95a98f8c4 --- /dev/null +++ b/release/scripts/presets/camera/RED_Dragon_5K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 25.60 +bpy.context.camera.sensor_height = 13.5 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/RED_Dragon_6K.py b/release/scripts/presets/camera/RED_Dragon_6K.py new file mode 100644 index 00000000000..80f7ad1bbb8 --- /dev/null +++ b/release/scripts/presets/camera/RED_Dragon_6K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 30.70 +bpy.context.camera.sensor_height = 15.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/RED_Helium_8K.py b/release/scripts/presets/camera/RED_Helium_8K.py new file mode 100644 index 00000000000..0f61d35a0f9 --- /dev/null +++ b/release/scripts/presets/camera/RED_Helium_8K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 29.90 +bpy.context.camera.sensor_height = 15.77 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/RED_Monstro_8K.py b/release/scripts/presets/camera/RED_Monstro_8K.py new file mode 100644 index 00000000000..86c382624ab --- /dev/null +++ b/release/scripts/presets/camera/RED_Monstro_8K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 40.96 +bpy.context.camera.sensor_height = 21.60 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/camera/Red_Epic.py b/release/scripts/presets/camera/Red_Epic.py deleted file mode 100644 index 5d71a69a33d..00000000000 --- a/release/scripts/presets/camera/Red_Epic.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 30.0 -bpy.context.camera.sensor_height = 15.0 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Red_One_2K.py b/release/scripts/presets/camera/Red_One_2K.py deleted file mode 100644 index 894aedcf9ea..00000000000 --- a/release/scripts/presets/camera/Red_One_2K.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 11.1 -bpy.context.camera.sensor_height = 6.24 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Red_One_3K.py b/release/scripts/presets/camera/Red_One_3K.py deleted file mode 100644 index 9ac84c1485a..00000000000 --- a/release/scripts/presets/camera/Red_One_3K.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 16.65 -bpy.context.camera.sensor_height = 9.36 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Red_One_4K.py b/release/scripts/presets/camera/Red_One_4K.py deleted file mode 100644 index 067322cd07e..00000000000 --- a/release/scripts/presets/camera/Red_One_4K.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 22.2 -bpy.context.camera.sensor_height = 12.6 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Samsung_Galaxy_S3.py b/release/scripts/presets/camera/Samsung_Galaxy_S3.py deleted file mode 100644 index 23eaea7cd27..00000000000 --- a/release/scripts/presets/camera/Samsung_Galaxy_S3.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.8 -bpy.context.camera.sensor_height = 3.6 -bpy.context.camera.lens = 3.70 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Samsung_Galaxy_S4.py b/release/scripts/presets/camera/Samsung_Galaxy_S4.py deleted file mode 100644 index cc929d26dac..00000000000 --- a/release/scripts/presets/camera/Samsung_Galaxy_S4.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.8 -bpy.context.camera.sensor_height = 3.6 -bpy.context.camera.lens = 4.20 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Sony_A55.py b/release/scripts/presets/camera/Sony_A55.py deleted file mode 100644 index 0468deb6d4c..00000000000 --- a/release/scripts/presets/camera/Sony_A55.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 23.4 -bpy.context.camera.sensor_height = 15.6 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Sony_EX1.py b/release/scripts/presets/camera/Sony_EX1.py deleted file mode 100644 index 3c6b235f21e..00000000000 --- a/release/scripts/presets/camera/Sony_EX1.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 6.97 -bpy.context.camera.sensor_height = 3.92 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Sony_F65.py b/release/scripts/presets/camera/Sony_F65.py deleted file mode 100644 index e62b3511836..00000000000 --- a/release/scripts/presets/camera/Sony_F65.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 24.33 -bpy.context.camera.sensor_height = 12.83 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Super_16_Film.py b/release/scripts/presets/camera/Super_16_Film.py deleted file mode 100644 index 4ca397a7e27..00000000000 --- a/release/scripts/presets/camera/Super_16_Film.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 12.52 -bpy.context.camera.sensor_height = 7.41 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Super_35_Film.py b/release/scripts/presets/camera/Super_35_Film.py deleted file mode 100644 index b22ff545c68..00000000000 --- a/release/scripts/presets/camera/Super_35_Film.py +++ /dev/null @@ -1,4 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 24.89 -bpy.context.camera.sensor_height = 18.66 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/iPhone_4.py b/release/scripts/presets/camera/iPhone_4.py deleted file mode 100644 index 1e43cd11494..00000000000 --- a/release/scripts/presets/camera/iPhone_4.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.54 -bpy.context.camera.sensor_height = 3.42 -bpy.context.camera.lens = 3.85 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/iPhone_4S.py b/release/scripts/presets/camera/iPhone_4S.py deleted file mode 100644 index 1139b7395b5..00000000000 --- a/release/scripts/presets/camera/iPhone_4S.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.54 -bpy.context.camera.sensor_height = 3.42 -bpy.context.camera.lens = 4.28 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/iPhone_5.py b/release/scripts/presets/camera/iPhone_5.py deleted file mode 100644 index a6b6bbc2ec5..00000000000 --- a/release/scripts/presets/camera/iPhone_5.py +++ /dev/null @@ -1,5 +0,0 @@ -import bpy -bpy.context.camera.sensor_width = 4.54 -bpy.context.camera.sensor_height = 3.42 -bpy.context.camera.lens = 4.10 -bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/1__colon__2.3_inch.py b/release/scripts/presets/tracking_camera/1__colon__2.3_inch.py deleted file mode 100644 index 9fcd40fbb65..00000000000 --- a/release/scripts/presets/tracking_camera/1__colon__2.3_inch.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 6.16 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/1__colon__2.5_inch.py b/release/scripts/presets/tracking_camera/1__colon__2.5_inch.py deleted file mode 100644 index 2f064e59838..00000000000 --- a/release/scripts/presets/tracking_camera/1__colon__2.5_inch.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 5.76 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/1_inch.py b/release/scripts/presets/tracking_camera/1_inch.py new file mode 100644 index 00000000000..72b039fb978 --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 13.2 +bpy.context.camera.sensor_height = 8.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py b/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py new file mode 100644 index 00000000000..38e09182de6 --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 7.18 +bpy.context.camera.sensor_height = 5.32 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py new file mode 100644 index 00000000000..4d55738f4ed --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 6.17 +bpy.context.camera.sensor_height = 4.55 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py new file mode 100644 index 00000000000..cbdb6f3cbe0 --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 5.76 +bpy.context.camera.sensor_height = 4.29 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py new file mode 100644 index 00000000000..5ccfa4ab555 --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 5.37 +bpy.context.camera.sensor_height = 4.04 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py b/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py new file mode 100644 index 00000000000..1963f7ec048 --- /dev/null +++ b/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 4.54 +bpy.context.camera.sensor_height = 3.42 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/2__colon__3_inch.py b/release/scripts/presets/tracking_camera/2__colon__3_inch.py deleted file mode 100644 index 8936e627d77..00000000000 --- a/release/scripts/presets/tracking_camera/2__colon__3_inch.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 9.6 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/2_slash_3_inch.py b/release/scripts/presets/tracking_camera/2_slash_3_inch.py new file mode 100644 index 00000000000..25b46016800 --- /dev/null +++ b/release/scripts/presets/tracking_camera/2_slash_3_inch.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 8.8 +bpy.context.camera.sensor_height = 6.6 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/4__colon__3_inch.py b/release/scripts/presets/tracking_camera/4__colon__3_inch.py deleted file mode 100644 index 2317715e1b4..00000000000 --- a/release/scripts/presets/tracking_camera/4__colon__3_inch.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 17.31 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/APS-C.py b/release/scripts/presets/tracking_camera/APS-C.py new file mode 100644 index 00000000000..84e40825248 --- /dev/null +++ b/release/scripts/presets/tracking_camera/APS-C.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 23.6 +bpy.context.camera.sensor_height = 15.6 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/APS-C_(Canon).py b/release/scripts/presets/tracking_camera/APS-C_(Canon).py new file mode 100644 index 00000000000..55f20ce0eac --- /dev/null +++ b/release/scripts/presets/tracking_camera/APS-C_(Canon).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 22.30 +bpy.context.camera.sensor_height = 14.90 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/APS-H_(Canon).py b/release/scripts/presets/tracking_camera/APS-H_(Canon).py new file mode 100644 index 00000000000..d63f733280b --- /dev/null +++ b/release/scripts/presets/tracking_camera/APS-H_(Canon).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 27.90 +bpy.context.camera.sensor_height = 18.60 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_16mm.py b/release/scripts/presets/tracking_camera/Analog_16mm.py new file mode 100644 index 00000000000..aa98eaf2408 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_16mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 10.26 +bpy.context.camera.sensor_height = 7.49 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_35mm.py b/release/scripts/presets/tracking_camera/Analog_35mm.py new file mode 100644 index 00000000000..a0dee1f0166 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_35mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 22 +bpy.context.camera.sensor_height = 16 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_65mm.py b/release/scripts/presets/tracking_camera/Analog_65mm.py new file mode 100644 index 00000000000..8de91ac0ee3 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_65mm.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 52.45 +bpy.context.camera.sensor_height = 23.01 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_IMAX.py b/release/scripts/presets/tracking_camera/Analog_IMAX.py new file mode 100644 index 00000000000..5a445f3de8c --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_IMAX.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 71.41 +bpy.context.camera.sensor_height = 52.63 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_Super_16.py b/release/scripts/presets/tracking_camera/Analog_Super_16.py new file mode 100644 index 00000000000..a340a31dc25 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_Super_16.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 12.35 +bpy.context.camera.sensor_height = 7.42 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Analog_Super_35.py b/release/scripts/presets/tracking_camera/Analog_Super_35.py new file mode 100644 index 00000000000..3c8f1837253 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Analog_Super_35.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 24.89 +bpy.context.camera.sensor_height = 18.66 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa.py b/release/scripts/presets/tracking_camera/Arri_Alexa.py deleted file mode 100644 index ded361ec965..00000000000 --- a/release/scripts/presets/tracking_camera/Arri_Alexa.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 23.76 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_65.py b/release/scripts/presets/tracking_camera/Arri_Alexa_65.py new file mode 100644 index 00000000000..b1467709949 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Arri_Alexa_65.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 54.12 +bpy.context.camera.sensor_height = 25.58 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py b/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py new file mode 100644 index 00000000000..1cde94fce8d --- /dev/null +++ b/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 36.70 +bpy.context.camera.sensor_height = 25.54 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py b/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py new file mode 100644 index 00000000000..0f61d35a0f9 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 29.90 +bpy.context.camera.sensor_height = 15.77 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py b/release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py deleted file mode 100644 index f84d0a19d22..00000000000 --- a/release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 15.81 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py new file mode 100644 index 00000000000..260bfbaf94f --- /dev/null +++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 12.48 +bpy.context.camera.sensor_height = 7.02 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py new file mode 100644 index 00000000000..dc057397828 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 18.96 +bpy.context.camera.sensor_height = 10.00 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py new file mode 100644 index 00000000000..a483f3d5f98 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 23.10 +bpy.context.camera.sensor_height = 12.99 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py deleted file mode 100644 index a9c81f47c21..00000000000 --- a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 12.48 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py b/release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py deleted file mode 100644 index d644d2a26c9..00000000000 --- a/release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 21.12 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py b/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py new file mode 100644 index 00000000000..c71e42d72d3 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 25.34 +bpy.context.camera.sensor_height = 14.25 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Blender.py b/release/scripts/presets/tracking_camera/Blender.py deleted file mode 100644 index 507cedac4fc..00000000000 --- a/release/scripts/presets/tracking_camera/Blender.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 32.0 -camera.units = 'MILLIMETERS' -camera.focal_length = 35.0 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Canon_1100D.py b/release/scripts/presets/tracking_camera/Canon_1100D.py deleted file mode 100644 index 96d6d456337..00000000000 --- a/release/scripts/presets/tracking_camera/Canon_1100D.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 22.2 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Canon_APS-C.py b/release/scripts/presets/tracking_camera/Canon_APS-C.py deleted file mode 100644 index cc4da545272..00000000000 --- a/release/scripts/presets/tracking_camera/Canon_APS-C.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 22.3 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Canon_APS-H.py b/release/scripts/presets/tracking_camera/Canon_APS-H.py deleted file mode 100644 index 853edd5dcba..00000000000 --- a/release/scripts/presets/tracking_camera/Canon_APS-H.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 27.90 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Canon_C300.py b/release/scripts/presets/tracking_camera/Canon_C300.py deleted file mode 100644 index 809f8f432f8..00000000000 --- a/release/scripts/presets/tracking_camera/Canon_C300.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 24.4 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Foveon_(Sigma).py b/release/scripts/presets/tracking_camera/Foveon_(Sigma).py new file mode 100644 index 00000000000..e6a1a0ed344 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Foveon_(Sigma).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 20.70 +bpy.context.camera.sensor_height = 13.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py b/release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py deleted file mode 100644 index 0f3da0b4d72..00000000000 --- a/release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 36 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Fullframe.py b/release/scripts/presets/tracking_camera/Fullframe.py new file mode 100644 index 00000000000..95fb4afc10b --- /dev/null +++ b/release/scripts/presets/tracking_camera/Fullframe.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 36 +bpy.context.camera.sensor_height = 24 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py b/release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py deleted file mode 100644 index 29851352284..00000000000 --- a/release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 6.16 -camera.units = 'MILLIMETERS' -camera.focal_length = 2.77 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py b/release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py deleted file mode 100644 index 9e08cf283a7..00000000000 --- a/release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 5.371 -camera.units = 'MILLIMETERS' -camera.focal_length = 2.77 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/GoPro_Hero3_White.py b/release/scripts/presets/tracking_camera/GoPro_Hero3_White.py deleted file mode 100644 index 6b1f9d97e81..00000000000 --- a/release/scripts/presets/tracking_camera/GoPro_Hero3_White.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 5.76 -camera.units = 'MILLIMETERS' -camera.focal_length = 2.77 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/MFT.py b/release/scripts/presets/tracking_camera/MFT.py new file mode 100644 index 00000000000..bc0dd49baa8 --- /dev/null +++ b/release/scripts/presets/tracking_camera/MFT.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 17.3 +bpy.context.camera.sensor_height = 13.0 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py b/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py new file mode 100644 index 00000000000..e9b16024b79 --- /dev/null +++ b/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 44 +bpy.context.camera.sensor_height = 33 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Nexus_5.py b/release/scripts/presets/tracking_camera/Nexus_5.py deleted file mode 100644 index 172c8e93bfd..00000000000 --- a/release/scripts/presets/tracking_camera/Nexus_5.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.5 -camera.units = 'MILLIMETERS' -camera.focal_length = 3.91 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Nikon_D3100.py b/release/scripts/presets/tracking_camera/Nikon_D3100.py deleted file mode 100644 index 44646f8b112..00000000000 --- a/release/scripts/presets/tracking_camera/Nikon_D3100.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 23.1 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Nikon_DX.py b/release/scripts/presets/tracking_camera/Nikon_DX.py deleted file mode 100644 index 8d9e3505e3f..00000000000 --- a/release/scripts/presets/tracking_camera/Nikon_DX.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 23.6 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py b/release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py deleted file mode 100644 index 49cc71fa5da..00000000000 --- a/release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.68 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1.5 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Panasonic_LX2.py b/release/scripts/presets/tracking_camera/Panasonic_LX2.py deleted file mode 100644 index f9ffcb8ec03..00000000000 --- a/release/scripts/presets/tracking_camera/Panasonic_LX2.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 8.5 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/RED_Dragon_5K.py b/release/scripts/presets/tracking_camera/RED_Dragon_5K.py new file mode 100644 index 00000000000..fa95a98f8c4 --- /dev/null +++ b/release/scripts/presets/tracking_camera/RED_Dragon_5K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 25.60 +bpy.context.camera.sensor_height = 13.5 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/RED_Dragon_6K.py b/release/scripts/presets/tracking_camera/RED_Dragon_6K.py new file mode 100644 index 00000000000..80f7ad1bbb8 --- /dev/null +++ b/release/scripts/presets/tracking_camera/RED_Dragon_6K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 30.70 +bpy.context.camera.sensor_height = 15.80 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/RED_Helium_8K.py b/release/scripts/presets/tracking_camera/RED_Helium_8K.py new file mode 100644 index 00000000000..0f61d35a0f9 --- /dev/null +++ b/release/scripts/presets/tracking_camera/RED_Helium_8K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 29.90 +bpy.context.camera.sensor_height = 15.77 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/RED_Monstro_8K.py b/release/scripts/presets/tracking_camera/RED_Monstro_8K.py new file mode 100644 index 00000000000..86c382624ab --- /dev/null +++ b/release/scripts/presets/tracking_camera/RED_Monstro_8K.py @@ -0,0 +1,4 @@ +import bpy +bpy.context.camera.sensor_width = 40.96 +bpy.context.camera.sensor_height = 21.60 +bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file diff --git a/release/scripts/presets/tracking_camera/Red_Epic.py b/release/scripts/presets/tracking_camera/Red_Epic.py deleted file mode 100644 index c0790e6baed..00000000000 --- a/release/scripts/presets/tracking_camera/Red_Epic.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 30.0 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Red_One_2K.py b/release/scripts/presets/tracking_camera/Red_One_2K.py deleted file mode 100644 index fa9585b5e08..00000000000 --- a/release/scripts/presets/tracking_camera/Red_One_2K.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 11.1 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Red_One_3K.py b/release/scripts/presets/tracking_camera/Red_One_3K.py deleted file mode 100644 index 5a1b7472109..00000000000 --- a/release/scripts/presets/tracking_camera/Red_One_3K.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 16.65 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Red_One_4K.py b/release/scripts/presets/tracking_camera/Red_One_4K.py deleted file mode 100644 index 96d6d456337..00000000000 --- a/release/scripts/presets/tracking_camera/Red_One_4K.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 22.2 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py b/release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py deleted file mode 100644 index d10994e45f5..00000000000 --- a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.8 -camera.units = 'MILLIMETERS' -camera.focal_length = 3.70 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py b/release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py deleted file mode 100644 index c5fef80b3de..00000000000 --- a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.8 -camera.units = 'MILLIMETERS' -camera.focal_length = 4.2 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Sony_A55.py b/release/scripts/presets/tracking_camera/Sony_A55.py deleted file mode 100644 index 26920d06f94..00000000000 --- a/release/scripts/presets/tracking_camera/Sony_A55.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 23.4 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Sony_EX1.py b/release/scripts/presets/tracking_camera/Sony_EX1.py deleted file mode 100644 index 2b99c91d221..00000000000 --- a/release/scripts/presets/tracking_camera/Sony_EX1.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 6.97 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Sony_F65.py b/release/scripts/presets/tracking_camera/Sony_F65.py deleted file mode 100644 index 7da93fd5d47..00000000000 --- a/release/scripts/presets/tracking_camera/Sony_F65.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 24.33 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Super_16.py b/release/scripts/presets/tracking_camera/Super_16.py deleted file mode 100644 index e94da9a99ba..00000000000 --- a/release/scripts/presets/tracking_camera/Super_16.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 12.52 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/Super_35.py b/release/scripts/presets/tracking_camera/Super_35.py deleted file mode 100644 index e07edc3a22c..00000000000 --- a/release/scripts/presets/tracking_camera/Super_35.py +++ /dev/null @@ -1,9 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 24.89 -camera.units = 'MILLIMETERS' -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/iPhone_4.py b/release/scripts/presets/tracking_camera/iPhone_4.py deleted file mode 100644 index 220e5e08147..00000000000 --- a/release/scripts/presets/tracking_camera/iPhone_4.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.54 -camera.units = 'MILLIMETERS' -camera.focal_length = 3.85 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/iPhone_4S.py b/release/scripts/presets/tracking_camera/iPhone_4S.py deleted file mode 100644 index 686cffc8f99..00000000000 --- a/release/scripts/presets/tracking_camera/iPhone_4S.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.54 -camera.units = 'MILLIMETERS' -camera.focal_length = 4.28 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 diff --git a/release/scripts/presets/tracking_camera/iPhone_5.py b/release/scripts/presets/tracking_camera/iPhone_5.py deleted file mode 100644 index d8e05da8425..00000000000 --- a/release/scripts/presets/tracking_camera/iPhone_5.py +++ /dev/null @@ -1,10 +0,0 @@ -import bpy -camera = bpy.context.edit_movieclip.tracking.camera - -camera.sensor_width = 4.54 -camera.units = 'MILLIMETERS' -camera.focal_length = 4.10 -camera.pixel_aspect = 1 -camera.k1 = 0.0 -camera.k2 = 0.0 -camera.k3 = 0.0 -- cgit v1.2.3 From b6b20c4d926ef6dbde7f557066490e6a97909b86 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 3 Jun 2021 20:06:41 +0200 Subject: GPencil: Change Fill extend lines icon The icon has been changed to `eye` because is more consistent with other areas. --- release/scripts/startup/bl_ui/space_view3d_toolbar.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 08d581bfa24..67f24d8185d 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1465,7 +1465,12 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): col.separator() row = col.row(align=True) row.prop(gp_settings, "extend_stroke_factor") - row.prop(gp_settings, "show_fill_extend", text="", icon='GRID') + if gp_settings.show_fill_extend: + icon = 'HIDE_OFF' + else: + icon = 'HIDE_ON' + + row.prop(gp_settings, "show_fill_extend", text="", icon=icon) col.separator() col.prop(gp_settings, "fill_leak", text="Leak Size") -- cgit v1.2.3 From 0037e08b0640d75bc6272bc0c73bd2ff6c02794f Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 4 Jun 2021 12:44:18 +0200 Subject: GPencil: Change Fill Boundary icon The icon has been changed to `eye` because is more consistent with other areas. --- release/scripts/startup/bl_ui/space_view3d_toolbar.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 67f24d8185d..4ae3310e510 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1456,7 +1456,11 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): elif brush.gpencil_tool == 'FILL': row = col.row(align=True) row.prop(gp_settings, "fill_draw_mode", text="Boundary") - row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID') + if gp_settings.show_fill_boundary: + icon = 'HIDE_OFF' + else: + icon = 'HIDE_ON' + row.prop(gp_settings, "show_fill_boundary", text="", icon=icon) col.separator() row = col.row(align=True) -- cgit v1.2.3 From b6d6d8a1aaef3737f6739788c3b48ba8a852d42d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 4 Jun 2021 15:54:07 +0200 Subject: LibOverride: Fix early break in some of the resync code. This `break` moved out of its braces at some point in the previous fixes/refctors... :( --- source/blender/blenkernel/intern/lib_override.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index beae8b59db4..c93971e7b11 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1507,8 +1507,8 @@ static void lib_override_library_main_resync_on_library_indirect_level( id_to->name, id_to->lib); lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level); + break; } - break; } } FOREACH_MAIN_ID_END; -- cgit v1.2.3 From 56005ef499894bcd48313e3df7ec965fe516c1dc Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 4 Jun 2021 16:00:51 +0200 Subject: Texture Paint: changing paint slots and viewport could go out of sync When changing to another texture paint slot, the texture displayed in the viewport should change accordingly (as well as the image displayed in the Image Editor). The procedure to find the texture to display in the viewport (BKE_texpaint_slot_material_find_node) could fail though because it assumed iterating nodes would always happen in the same order (it was index based). This is not the case though, nodes can get sorted differently based on selection (see ED_node_sort). Now check the actual image being referenced in the paint slot for comparison. ref T88788 (probably enough to call this a fix, the other issue(s) mentioned in the report are more likely a feature request) Reviewed By: mano-wii Maniphest Tasks: T88788 Differential Revision: https://developer.blender.org/D11496 --- source/blender/blenkernel/intern/material.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 557fe5f7e75..468f735257a 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1513,16 +1513,16 @@ void BKE_texpaint_slots_refresh_object(Scene *scene, struct Object *ob) } struct FindTexPaintNodeData { - bNode *node; - short iter_index; - short index; + Image *ima; + bNode *r_node; }; static bool texpaint_slot_node_find_cb(bNode *node, void *userdata) { struct FindTexPaintNodeData *find_data = userdata; - if (find_data->iter_index++ == find_data->index) { - find_data->node = node; + Image *ima = (Image *)node->id; + if (find_data->ima == ima) { + find_data->r_node = node; return false; } @@ -1531,10 +1531,10 @@ static bool texpaint_slot_node_find_cb(bNode *node, void *userdata) bNode *BKE_texpaint_slot_material_find_node(Material *ma, short texpaint_slot) { - struct FindTexPaintNodeData find_data = {NULL, 0, texpaint_slot}; + struct FindTexPaintNodeData find_data = {ma->texpaintslot[texpaint_slot].ima, NULL}; ntree_foreach_texnode_recursive(ma->nodetree, texpaint_slot_node_find_cb, &find_data); - return find_data.node; + return find_data.r_node; } /* r_col = current value, col = new value, (fac == 0) is no change */ -- cgit v1.2.3 From eb030204f198fe1b933380c63bd40f3dba58c105 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Fri, 4 Jun 2021 09:16:03 -0600 Subject: windows/deps: Fix TBB build issues. rB847579b42250 updated the TBB build script which had some unintended consequences for windows as the directory layout slightly changed. This change adjusts the builder to the new structure, there are no version/functional changes. --- build_files/build_environment/cmake/embree.cmake | 6 ++++++ build_files/build_environment/cmake/tbb.cmake | 25 ++++++++++++------------ build_files/cmake/platform/platform_win32.cmake | 2 +- source/creator/CMakeLists.txt | 12 ++++++------ 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/build_files/build_environment/cmake/embree.cmake b/build_files/build_environment/cmake/embree.cmake index cd693d766dc..b1d5dff91a0 100644 --- a/build_files/build_environment/cmake/embree.cmake +++ b/build_files/build_environment/cmake/embree.cmake @@ -43,6 +43,12 @@ endif() if(WIN32) set(EMBREE_BUILD_DIR ${BUILD_MODE}/) + if(BUILD_MODE STREQUAL Debug) + list(APPEND EMBREE_EXTRA_ARGS + -DEMBREE_TBBMALLOC_LIBRARY_NAME=tbbmalloc_debug + -DEMBREE_TBB_LIBRARY_NAME=tbb_debug + ) + endif() else() set(EMBREE_BUILD_DIR) endif() diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake index 44307cd2afb..005616a9f8d 100644 --- a/build_files/build_environment/cmake/tbb.cmake +++ b/build_files/build_environment/cmake/tbb.cmake @@ -22,6 +22,7 @@ if(WIN32) -DTBB_BUILD_TBBMALLOC_PROXY=On -DTBB_BUILD_STATIC=Off -DTBB_BUILD_TESTS=Off + -DCMAKE_DEBUG_POSTFIX=_debug ) set(TBB_LIBRARY tbb) set(TBB_STATIC_LIBRARY Off) @@ -55,17 +56,17 @@ if(WIN32) ExternalProject_Add_Step(external_tbb after_install # findtbb.cmake in some deps *NEEDS* to find tbb_debug.lib even if they are not going to use it # to make that test pass, we place a copy with the right name in the lib folder. - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${HARVEST_TARGET}/tbb/lib/tbb_debug.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.dll ${HARVEST_TARGET}/tbb/lib/tbb_debug.dll - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${LIBDIR}/tbb/lib/tbb_debug.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb.dll ${LIBDIR}/tbb/bin/tbb_debug.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc.dll ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll # Normal collection of build artifacts COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${HARVEST_TARGET}/tbb/lib/tbb.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.dll ${HARVEST_TARGET}/tbb/lib/tbb.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb.dll ${HARVEST_TARGET}/tbb/bin/tbb.dll COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc.dll COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_proxy.dll COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/tbb/include/ ${HARVEST_TARGET}/tbb/include/ DEPENDEES install ) @@ -76,11 +77,11 @@ if(WIN32) # to make that test pass, we place a copy with the right name in the lib folder. COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${LIBDIR}/tbb/lib/tbb.lib # Normal collection of build artifacts - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${HARVEST_TARGET}/tbb/lib/debug/tbb_debug.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.dll ${HARVEST_TARGET}/tbb/lib/debug/tbb_debug.dll - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy_debug.lib - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/debug/tbbmalloc.dll - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/lib/debug/tbbmalloc_proxy.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${HARVEST_TARGET}/tbb/lib/tbb_debug.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb_debug.dll ${HARVEST_TARGET}/tbb/bin/tbb_debug.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy_debug.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy_debug.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_debug.dll + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_proxy_debug.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_proxy_debug.dll DEPENDEES install ) endif() diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 497fd179aaf..fe5b179d6d1 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -675,7 +675,7 @@ if(WITH_SYSTEM_AUDASPACE) endif() if(WITH_TBB) - set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/debug/tbb_debug.lib) + set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib) set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include) set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR}) if(WITH_TBB_MALLOC_PROXY) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 92cc4ae297a..519b3781103 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -883,13 +883,13 @@ elseif(WIN32) if(WITH_TBB) install( FILES - ${LIBDIR}/tbb/lib/tbb.dll + ${LIBDIR}/tbb/bin/tbb.dll DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( FILES - ${LIBDIR}/tbb/lib/debug/tbb_debug.dll + ${LIBDIR}/tbb/bin/tbb_debug.dll DESTINATION "." CONFIGURATIONS Debug ) @@ -897,15 +897,15 @@ elseif(WIN32) if(WITH_TBB_MALLOC_PROXY) install( FILES - ${LIBDIR}/tbb/lib/tbbmalloc.dll - ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll + ${LIBDIR}/tbb/bin/tbbmalloc.dll + ${LIBDIR}/tbb/bin/tbbmalloc_proxy.dll DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( FILES - ${LIBDIR}/tbb/lib/debug/tbbmalloc.dll - ${LIBDIR}/tbb/lib/debug/tbbmalloc_proxy.dll + ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll + ${LIBDIR}/tbb/bin/tbbmalloc_proxy_debug.dll DESTINATION "." CONFIGURATIONS Debug ) -- cgit v1.2.3 From 00073651d420c852b271127fe453d2170471321a Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Fri, 4 Jun 2021 16:53:50 +0100 Subject: Nodes: Add Multiply Add to Vector Math nodes Cycles, Eevee, OSL, Geo, Attribute This operator provides consistency with the standard math node. Allows users to use a single node instead of two nodes for this common operation. Reviewed By: HooglyBoogly, brecht Differential Revision: https://developer.blender.org/D10808 --- intern/cycles/kernel/shaders/node_vector_math.osl | 3 +++ intern/cycles/kernel/svm/svm_math.h | 3 ++- intern/cycles/kernel/svm/svm_math_util.h | 3 +++ intern/cycles/kernel/svm/svm_types.h | 1 + intern/cycles/render/nodes.cpp | 4 +++- release/datafiles/locale | 2 +- .../shaders/material/gpu_shader_material_vector_math.glsl | 6 ++++++ source/blender/makesdna/DNA_node_types.h | 1 + source/blender/makesrna/intern/rna_nodetree.c | 1 + source/blender/nodes/NOD_math_functions.hh | 2 ++ .../nodes/geometry/nodes/node_geo_attribute_vector_math.cc | 9 +++++++-- source/blender/nodes/intern/math_functions.cc | 2 ++ .../blender/nodes/shader/nodes/node_shader_vector_math.cc | 13 +++++++++++-- 13 files changed, 43 insertions(+), 7 deletions(-) diff --git a/intern/cycles/kernel/shaders/node_vector_math.osl b/intern/cycles/kernel/shaders/node_vector_math.osl index 3963c23ea9c..c08d75b99ef 100644 --- a/intern/cycles/kernel/shaders/node_vector_math.osl +++ b/intern/cycles/kernel/shaders/node_vector_math.osl @@ -52,6 +52,9 @@ shader node_vector_math(string math_type = "add", else if (math_type == "faceforward") { Vector = compatible_faceforward(Vector1, Vector2, Vector3); } + else if (math_type == "multiply_add") { + Vector = Vector1 * Vector2 + Vector3; + } else if (math_type == "dot_product") { Value = dot(Vector1, Vector2); } diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/svm_math.h index dda2e50f916..733ea28f9e5 100644 --- a/intern/cycles/kernel/svm/svm_math.h +++ b/intern/cycles/kernel/svm/svm_math.h @@ -58,7 +58,8 @@ ccl_device void svm_node_vector_math(KernelGlobals *kg, float3 vector; /* 3 Vector Operators */ - if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD) { + if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD || + type == NODE_VECTOR_MATH_MULTIPLY_ADD) { uint4 extra_node = read_node(kg, offset); c = stack_load_float3(stack, extra_node.x); } diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/svm_math_util.h index 19fb1da5a1f..9e654f2247f 100644 --- a/intern/cycles/kernel/svm/svm_math_util.h +++ b/intern/cycles/kernel/svm/svm_math_util.h @@ -52,6 +52,9 @@ ccl_device void svm_vector_math(float *value, case NODE_VECTOR_MATH_FACEFORWARD: *vector = faceforward(a, b, c); break; + case NODE_VECTOR_MATH_MULTIPLY_ADD: + *vector = a * b + c; + break; case NODE_VECTOR_MATH_DOT_PRODUCT: *value = dot(a, b); break; diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 64a8f82a094..062afcfa5ac 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -341,6 +341,7 @@ typedef enum NodeVectorMathType { NODE_VECTOR_MATH_TANGENT, NODE_VECTOR_MATH_REFRACT, NODE_VECTOR_MATH_FACEFORWARD, + NODE_VECTOR_MATH_MULTIPLY_ADD, } NodeVectorMathType; typedef enum NodeClampType { diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index f3d420c6fcb..5792d2458d1 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -6093,6 +6093,7 @@ NODE_DEFINE(VectorMathNode) type_enum.insert("reflect", NODE_VECTOR_MATH_REFLECT); type_enum.insert("refract", NODE_VECTOR_MATH_REFRACT); type_enum.insert("faceforward", NODE_VECTOR_MATH_FACEFORWARD); + type_enum.insert("multiply_add", NODE_VECTOR_MATH_MULTIPLY_ADD); type_enum.insert("dot_product", NODE_VECTOR_MATH_DOT_PRODUCT); @@ -6165,7 +6166,8 @@ void VectorMathNode::compile(SVMCompiler &compiler) int vector_stack_offset = compiler.stack_assign_if_linked(vector_out); /* 3 Vector Operators */ - if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD) { + if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD || + math_type == NODE_VECTOR_MATH_MULTIPLY_ADD) { ShaderInput *vector3_in = input("Vector3"); int vector3_stack_offset = compiler.stack_assign(vector3_in); compiler.add_node( diff --git a/release/datafiles/locale b/release/datafiles/locale index 5ab29b1331d..2cef4877edc 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 5ab29b1331d2103dae634b987f121c4599459d7f +Subproject commit 2cef4877edc40875978c4e95322bb5193f5815bf diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl index 60ed098beb3..4ad5d4232de 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl @@ -150,3 +150,9 @@ void vector_math_faceforward( { outVector = faceforward(a, b, c); } + +void vector_math_multiply_add( + vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue) +{ + outVector = a * b + c; +} diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 0fa1d1a74ac..1ab6c5e52a6 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1633,6 +1633,7 @@ typedef enum NodeVectorMathOperation { NODE_VECTOR_MATH_TANGENT = 23, NODE_VECTOR_MATH_REFRACT = 24, NODE_VECTOR_MATH_FACEFORWARD = 25, + NODE_VECTOR_MATH_MULTIPLY_ADD = 26, } NodeVectorMathOperation; /* Boolean math node operations. */ diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index f5cbb694554..2bafbd57e11 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -248,6 +248,7 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = { {NODE_VECTOR_MATH_SUBTRACT, "SUBTRACT", 0, "Subtract", "A - B"}, {NODE_VECTOR_MATH_MULTIPLY, "MULTIPLY", 0, "Multiply", "Entry-wise multiply"}, {NODE_VECTOR_MATH_DIVIDE, "DIVIDE", 0, "Divide", "Entry-wise divide"}, + {NODE_VECTOR_MATH_MULTIPLY_ADD, "MULTIPLY_ADD", 0, "Multiply Add", "A * B + C"}, {0, "", ICON_NONE, NULL, NULL}, {NODE_VECTOR_MATH_CROSS_PRODUCT, "CROSS_PRODUCT", 0, "Cross Product", "A cross B"}, {NODE_VECTOR_MATH_PROJECT, "PROJECT", 0, "Project", "Project A onto B"}, diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh index 45de1816549..f3eb1c24087 100644 --- a/source/blender/nodes/NOD_math_functions.hh +++ b/source/blender/nodes/NOD_math_functions.hh @@ -347,6 +347,8 @@ inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOpera }; switch (operation) { + case NODE_VECTOR_MATH_MULTIPLY_ADD: + return dispatch([](float3 a, float3 b, float3 c) { return a * b + c; }); case NODE_VECTOR_MATH_WRAP: return dispatch([](float3 a, float3 b, float3 c) { return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z)); 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 8877af445f9..b04e04d1cb7 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 @@ -59,8 +59,11 @@ static bool operation_use_input_b(const NodeVectorMathOperation operation) static bool operation_use_input_c(const NodeVectorMathOperation operation) { - return ELEM( - operation, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_REFRACT, NODE_VECTOR_MATH_FACEFORWARD); + return ELEM(operation, + NODE_VECTOR_MATH_WRAP, + NODE_VECTOR_MATH_REFRACT, + NODE_VECTOR_MATH_FACEFORWARD, + NODE_VECTOR_MATH_MULTIPLY_ADD); } static void geo_node_attribute_vector_math_layout(uiLayout *layout, @@ -137,6 +140,7 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op case NODE_VECTOR_MATH_TANGENT: case NODE_VECTOR_MATH_REFRACT: case NODE_VECTOR_MATH_FACEFORWARD: + case NODE_VECTOR_MATH_MULTIPLY_ADD: return CD_PROP_FLOAT3; case NODE_VECTOR_MATH_DOT_PRODUCT: case NODE_VECTOR_MATH_DISTANCE: @@ -495,6 +499,7 @@ static void attribute_vector_math_calc(GeometryComponent &component, break; case NODE_VECTOR_MATH_WRAP: case NODE_VECTOR_MATH_FACEFORWARD: + case NODE_VECTOR_MATH_MULTIPLY_ADD: do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed(), attribute_b->typed(), attribute_c->typed(), diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc index fb34144abf6..aa23777b664 100644 --- a/source/blender/nodes/intern/math_functions.cc +++ b/source/blender/nodes/intern/math_functions.cc @@ -209,6 +209,8 @@ const FloatMathOperationInfo *get_float3_math_operation_info(const int operation RETURN_OPERATION_INFO("Refract", "vector_math_refract"); case NODE_VECTOR_MATH_FACEFORWARD: RETURN_OPERATION_INFO("Faceforward", "vector_math_faceforward"); + case NODE_VECTOR_MATH_MULTIPLY_ADD: + RETURN_OPERATION_INFO("Multiply Add", "vector_math_multiply_add"); } #undef RETURN_OPERATION_INFO diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 472f903e1a5..419a11201aa 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -94,6 +94,8 @@ static const char *gpu_shader_get_name(int mode) return "vector_math_refract"; case NODE_VECTOR_MATH_FACEFORWARD: return "vector_math_faceforward"; + case NODE_VECTOR_MATH_MULTIPLY_ADD: + return "vector_math_multiply_add"; } return nullptr; @@ -134,8 +136,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node NODE_VECTOR_MATH_ABSOLUTE, NODE_VECTOR_MATH_FRACTION, NODE_VECTOR_MATH_NORMALIZE)); - nodeSetSocketAvailability( - sockC, ELEM(node->custom1, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_FACEFORWARD)); + nodeSetSocketAvailability(sockC, + ELEM(node->custom1, + NODE_VECTOR_MATH_WRAP, + NODE_VECTOR_MATH_FACEFORWARD, + NODE_VECTOR_MATH_MULTIPLY_ADD)); nodeSetSocketAvailability(sockScale, ELEM(node->custom1, NODE_VECTOR_MATH_SCALE, NODE_VECTOR_MATH_REFRACT)); nodeSetSocketAvailability(sockVector, @@ -154,6 +159,10 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node node_sock_label_clear(sockC); node_sock_label_clear(sockScale); switch (node->custom1) { + case NODE_VECTOR_MATH_MULTIPLY_ADD: + node_sock_label(sockB, "Multiplier"); + node_sock_label(sockC, "Addend"); + break; case NODE_VECTOR_MATH_FACEFORWARD: node_sock_label(sockB, "Incident"); node_sock_label(sockC, "Reference"); -- cgit v1.2.3 From c2fa36999ff25ce2d011971a460d7efa11705e57 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 28 May 2021 18:18:13 +1000 Subject: Edit Mesh: partial updates for normal and face tessellation This patch exposes functionality for performing partial mesh updates for normal calculation and face tessellation while transforming a mesh. The partial update data only needs to be generated once, afterwards the cached connectivity information can be reused (with the exception of changing proportional editing radius). Currently this is only used for transform, in the future it could be used for other operators as well as the transform panel. The best-case overall speedup while transforming geometry is about 1.45x since the time to update a small number of normals and faces is negligible. For an additional speedup partial face tessellation is multi-threaded, this gives ~15x speedup on my system (timing tessellation alone). Exact results depend on the number of CPU cores available. Ref D11494 Reviewed By: mano-wii --- source/blender/blenkernel/BKE_editmesh.h | 3 + source/blender/blenkernel/intern/editmesh.c | 8 + source/blender/bmesh/CMakeLists.txt | 2 + source/blender/bmesh/bmesh.h | 1 + source/blender/bmesh/intern/bmesh_mesh.c | 151 +++++++++++- source/blender/bmesh/intern/bmesh_mesh.h | 3 + .../bmesh/intern/bmesh_mesh_partial_update.c | 272 +++++++++++++++++++++ .../bmesh/intern/bmesh_mesh_partial_update.h | 69 ++++++ .../blender/bmesh/intern/bmesh_mesh_tessellate.c | 100 ++++++++ .../blender/bmesh/intern/bmesh_mesh_tessellate.h | 6 + source/blender/bmesh/intern/bmesh_polygon.h | 1 + source/blender/editors/transform/transform.h | 1 + .../editors/transform/transform_convert_mesh.c | 252 +++++++++++++++---- 13 files changed, 819 insertions(+), 50 deletions(-) create mode 100644 source/blender/bmesh/intern/bmesh_mesh_partial_update.c create mode 100644 source/blender/bmesh/intern/bmesh_mesh_partial_update.h diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 2fb713a4299..3a1eedfd807 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -33,6 +33,7 @@ extern "C" { struct BMLoop; struct BMesh; +struct BMPartialUpdate; struct BoundBox; struct Depsgraph; struct Mesh; @@ -85,6 +86,8 @@ typedef struct BMEditMesh { /* editmesh.c */ void BKE_editmesh_looptri_calc(BMEditMesh *em); +void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo); + BMEditMesh *BKE_editmesh_create(BMesh *bm, const bool do_tessellate); BMEditMesh *BKE_editmesh_copy(BMEditMesh *em); BMEditMesh *BKE_editmesh_from_object(struct Object *ob); diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index bd76357617a..472de1f3c77 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -149,6 +149,14 @@ void BKE_editmesh_looptri_calc(BMEditMesh *em) #endif } +void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo) +{ + BLI_assert(em->tottri == poly_to_tri_count(em->bm->totface, em->bm->totloop)); + BLI_assert(em->looptris != NULL); + + BM_mesh_calc_tessellation_with_partial(em->bm, em->looptris, bmpinfo); +} + void BKE_editmesh_free_derivedmesh(BMEditMesh *em) { if (em->mesh_eval_cage) { diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index af47b9557ef..4bd88ae62d7 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -104,6 +104,8 @@ set(SRC intern/bmesh_mesh_duplicate.h intern/bmesh_mesh_tessellate.c intern/bmesh_mesh_tessellate.h + intern/bmesh_mesh_partial_update.c + intern/bmesh_mesh_partial_update.h intern/bmesh_mesh_validate.c intern/bmesh_mesh_validate.h intern/bmesh_mods.c diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index a7e4e011ae2..d8c3f4ce556 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -215,6 +215,7 @@ extern "C" { #include "intern/bmesh_mesh.h" #include "intern/bmesh_mesh_convert.h" #include "intern/bmesh_mesh_duplicate.h" +#include "intern/bmesh_mesh_partial_update.h" #include "intern/bmesh_mesh_tessellate.h" #include "intern/bmesh_mesh_validate.h" #include "intern/bmesh_mods.h" diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index 69bb61a4f7d..d4357fa561b 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -373,15 +373,16 @@ typedef struct BMVertsCalcNormalsData { float (*vnos)[3]; } BMVertsCalcNormalsData; -static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp_f) +static void mesh_verts_calc_normals_accum( + BMFace *f, + const float *f_no, + const float (*edgevec)[3], + + /* Read-write data, protected by an atomic-based fake spin-lock like system. */ + float (*vnos)[3]) { #define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb)) - BMVertsCalcNormalsData *data = userdata; - BMFace *f = (BMFace *)mp_f; - - const float *f_no = data->fnos ? data->fnos[BM_elem_index_get(f)] : f->no; - BMLoop *l_first, *l_iter; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { @@ -391,8 +392,8 @@ static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp /* calculate the dot product of the two edges that * meet at the loop's vertex */ - e1diff = data->edgevec[BM_elem_index_get(l_iter->prev->e)]; - e2diff = data->edgevec[BM_elem_index_get(l_iter->e)]; + e1diff = edgevec[BM_elem_index_get(l_iter->prev->e)]; + e2diff = edgevec[BM_elem_index_get(l_iter->e)]; dotprod = dot_v3v3(e1diff, e2diff); /* edge vectors are calculated from e->v1 to e->v2, so @@ -410,7 +411,7 @@ static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp } /* accumulate weighted face normal into the vertex's normal */ - float *v_no = data->vnos ? data->vnos[BM_elem_index_get(l_iter->v)] : l_iter->v->no; + float *v_no = vnos ? vnos[BM_elem_index_get(l_iter->v)] : l_iter->v->no; /* This block is a lockless threadsafe madd_v3_v3fl. * It uses the first float of the vector as a sort of cheap spin-lock, @@ -447,6 +448,14 @@ static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp #undef FLT_EQ_NONAN } +static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp_f) +{ + BMVertsCalcNormalsData *data = userdata; + BMFace *f = (BMFace *)mp_f; + const float *f_no = data->fnos ? data->fnos[BM_elem_index_get(f)] : f->no; + mesh_verts_calc_normals_accum(f, f_no, data->edgevec, data->vnos); +} + static void mesh_verts_calc_normals_normalize_cb(void *userdata, MempoolIterData *mp_v) { BMVertsCalcNormalsData *data = userdata; @@ -529,6 +538,130 @@ void BM_mesh_normals_update(BMesh *bm) MEM_freeN(edgevec); } +static void mesh_faces_parallel_range_calc_normals_cb( + void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BMFace *f = ((BMFace **)userdata)[iter]; + BM_face_normal_update(f); +} + +static void mesh_edges_parallel_range_calc_vectors_cb( + void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BMEdge *e = ((BMEdge **)((void **)userdata)[0])[iter]; + float *r_edgevec = ((float(*)[3])((void **)userdata)[1])[iter]; + sub_v3_v3v3(r_edgevec, e->v1->co, e->v2->co); + normalize_v3(r_edgevec); +} + +static void mesh_verts_parallel_range_calc_normals_accum_cb( + void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BMFace *f = ((BMFace **)((void **)userdata)[0])[iter]; + const float(*edgevec)[3] = (float(*)[3])((void **)userdata)[1]; + mesh_verts_calc_normals_accum(f, f->no, edgevec, NULL); +} + +static void mesh_verts_parallel_range_calc_normals_normalize_cb( + void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BMVert *v = ((BMVert **)userdata)[iter]; + if (UNLIKELY(normalize_v3(v->no) == 0.0f)) { + normalize_v3_v3(v->no, v->co); + } +} + +/** + * A version of #BM_mesh_normals_update that updates a subset of geometry, + * used to avoid the overhead of updating everything. + */ +void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpinfo) +{ + BLI_assert(bmpinfo->params.do_normals); + + BMVert **verts = bmpinfo->verts; + BMEdge **edges = bmpinfo->edges; + BMFace **faces = bmpinfo->faces; + const int verts_len = bmpinfo->verts_len; + const int edges_len = bmpinfo->edges_len; + const int faces_len = bmpinfo->faces_len; + const int faces_len_normal_calc_accumulate = bmpinfo->faces_len_normal_calc_accumulate; + + float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * edges_len, __func__); + + for (int i = 0; i < verts_len; i++) { + zero_v3(verts[i]->no); + } + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + { + /* Faces. */ + BLI_task_parallel_range( + 0, faces_len, faces, mesh_faces_parallel_range_calc_normals_cb, &settings); + } + + /* Temporarily override the edge indices, + * storing the correct indices in the case they're not dirty. + * + * \note in most cases indices are modified and #BMesh.elem_index_dirty is set. + * This is an exceptional case where indices are restored because the worst case downside + * of marking the edge indices dirty would require a full loop over all edges to + * correct the indices in other functions which need them to be valid. + * When moving a few vertices on a high poly mesh setting and restoring connected + * edges has very little overhead compared with restoring all edge indices. */ + int *edge_index_value = NULL; + if ((bm->elem_index_dirty & BM_EDGE) == 0) { + edge_index_value = MEM_mallocN(sizeof(*edge_index_value) * edges_len, __func__); + + for (int i = 0; i < edges_len; i++) { + BMEdge *e = edges[i]; + edge_index_value[i] = BM_elem_index_get(e); + BM_elem_index_set(e, i); /* set_dirty! (restore before this function exits). */ + } + } + else { + for (int i = 0; i < edges_len; i++) { + BMEdge *e = edges[i]; + BM_elem_index_set(e, i); /* set_dirty! (already dirty) */ + } + } + + { + /* Verts. */ + + /* Compute normalized direction vectors for each edge. + * Directions will be used for calculating the weights of the face normals on the vertex + * normals. */ + void *data[2] = {edges, edgevec}; + BLI_task_parallel_range( + 0, edges_len, data, mesh_edges_parallel_range_calc_vectors_cb, &settings); + + /* Add weighted face normals to vertices. */ + data[0] = faces; + BLI_task_parallel_range(0, + faces_len_normal_calc_accumulate, + data, + mesh_verts_parallel_range_calc_normals_accum_cb, + &settings); + + /* Normalize the accumulated vertex normals. */ + BLI_task_parallel_range( + 0, verts_len, verts, mesh_verts_parallel_range_calc_normals_normalize_cb, &settings); + } + + if (edge_index_value != NULL) { + for (int i = 0; i < edges_len; i++) { + BMEdge *e = edges[i]; + BM_elem_index_set(e, edge_index_value[i]); /* set_ok (restore) */ + } + + MEM_freeN(edge_index_value); + } + + MEM_freeN(edgevec); +} + /** * \brief BMesh Compute Normals from/to external data. * diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index c1c2f17d7c1..708786a4c55 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -25,6 +25,7 @@ struct BMAllocTemplate; struct BMLoopNorEditDataArray; struct MLoopNorSpaceArray; +struct BMPartialUpdate; void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); @@ -41,6 +42,8 @@ void BM_mesh_data_free(BMesh *bm); void BM_mesh_clear(BMesh *bm); void BM_mesh_normals_update(BMesh *bm); +void BM_mesh_normals_update_with_partial(BMesh *bm, const struct BMPartialUpdate *bmpinfo); + void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*vcos)[3], diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c new file mode 100644 index 00000000000..b87d9811049 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c @@ -0,0 +1,272 @@ +/* + * 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. + */ + +/** \file + * \ingroup bmesh + * + * Generate data needed for partially updating mesh information. + * Currently this is used for normals and tessellation. + * + * Transform is the obvious use case where there is no need to update normals or tessellation + * for geometry which has not been modified. + * + * In the future this could be integrated into GPU updates too. + * + * Potential Improvements + * ====================== + * + * Some calculations could be significantly limited in the case of affine transformations + * (tessellation is an obvious candidate). Where only faces which have a mix + * of tagged and untagged vertices would need to be recalculated. + * + * In general this would work well besides some corner cases such as scaling to zero. + * Although the exact result may depend on the normal (for N-GONS), + * so for now update the tessellation of all tagged geometry. + */ + +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_bitmap.h" +#include "BLI_math_vector.h" + +#include "bmesh.h" + +/** + * Grow by 1.5x (rounding up). + * + * \note Use conservative reallocation since the initial sizes reserved + * may be close to (or exactly) the number of elements needed. + */ +#define GROW(len_alloc) ((len_alloc) + ((len_alloc) - ((len_alloc) / 2))) +#define GROW_ARRAY(mem, len_alloc) \ + { \ + mem = MEM_reallocN(mem, (sizeof(*mem)) * ((len_alloc) = GROW(len_alloc))); \ + } \ + ((void)0) + +#define GROW_ARRAY_AS_NEEDED(mem, len_alloc, index) \ + if (UNLIKELY(len_alloc == index)) { \ + GROW_ARRAY(mem, len_alloc); \ + } + +BLI_INLINE bool partial_elem_vert_ensure(BMPartialUpdate *bmpinfo, + BLI_bitmap *verts_tag, + BMVert *v) +{ + const int i = BM_elem_index_get(v); + if (!BLI_BITMAP_TEST(verts_tag, i)) { + BLI_BITMAP_ENABLE(verts_tag, i); + GROW_ARRAY_AS_NEEDED(bmpinfo->verts, bmpinfo->verts_len_alloc, bmpinfo->verts_len); + bmpinfo->verts[bmpinfo->verts_len++] = v; + return true; + } + return false; +} + +BLI_INLINE bool partial_elem_edge_ensure(BMPartialUpdate *bmpinfo, + BLI_bitmap *edges_tag, + BMEdge *e) +{ + const int i = BM_elem_index_get(e); + if (!BLI_BITMAP_TEST(edges_tag, i)) { + BLI_BITMAP_ENABLE(edges_tag, i); + GROW_ARRAY_AS_NEEDED(bmpinfo->edges, bmpinfo->edges_len_alloc, bmpinfo->edges_len); + bmpinfo->edges[bmpinfo->edges_len++] = e; + return true; + } + return false; +} + +BLI_INLINE bool partial_elem_face_ensure(BMPartialUpdate *bmpinfo, + BLI_bitmap *faces_tag, + BMFace *f) +{ + const int i = BM_elem_index_get(f); + if (!BLI_BITMAP_TEST(faces_tag, i)) { + BLI_BITMAP_ENABLE(faces_tag, i); + GROW_ARRAY_AS_NEEDED(bmpinfo->faces, bmpinfo->faces_len_alloc, bmpinfo->faces_len); + bmpinfo->faces[bmpinfo->faces_len++] = f; + return true; + } + return false; +} + +BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, + const BMPartialUpdate_Params *params, + const int verts_len, + bool (*filter_fn)(BMVert *, void *user_data), + void *user_data) +{ + /* The caller is doing something wrong if this isn't the case. */ + BLI_assert(verts_len <= bm->totvert); + + BMPartialUpdate *bmpinfo = MEM_callocN(sizeof(*bmpinfo), __func__); + + /* Reserve more edges than vertices since it's common for a grid topology + * to use around twice as many edges as vertices. */ + const int default_verts_len_alloc = verts_len; + const int default_edges_len_alloc = min_ii(bm->totedge, verts_len * 2); + const int default_faces_len_alloc = min_ii(bm->totface, verts_len); + + /* Allocate tags instead of using #BM_ELEM_TAG because the caller may already be using tags. + * Further, walking over all geometry to clear the tags isn't so efficient. */ + BLI_bitmap *verts_tag = NULL; + BLI_bitmap *edges_tag = NULL; + BLI_bitmap *faces_tag = NULL; + + /* Set vert inline. */ + BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE)); + + if (params->do_normals || params->do_tessellate) { + /* - Extend to all vertices connected faces: + * In the case of tessellation this is enough. + * + * In the case of vertex normal calculation, + * All the relevant connectivity data can be accessed from the faces + * (there is no advantage in storing connected edges or vertices in this pass). + * + * NOTE: In the future it may be useful to differentiate between vertices + * that are directly marked (by the filter function when looping over all vertices). + * And vertices marked from indirect connections. + * This would require an extra tag array, so avoid this unless it's needed. + */ + + /* Faces. */ + if (bmpinfo->faces == NULL) { + bmpinfo->faces_len_alloc = default_faces_len_alloc; + bmpinfo->faces = MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__); + faces_tag = BLI_BITMAP_NEW((size_t)bm->totface, __func__); + } + + BMVert *v; + BMIter iter; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + BM_elem_index_set(v, i); /* set_inline */ + if (!filter_fn(v, user_data)) { + continue; + } + BMEdge *e_iter = v->e; + if (e_iter != NULL) { + /* Loop over edges. */ + BMEdge *e_first = v->e; + do { + BMLoop *l_iter = e_iter->l; + if (e_iter->l != NULL) { + BMLoop *l_first = e_iter->l; + /* Loop over radial loops. */ + do { + if (l_iter->v == v) { + partial_elem_face_ensure(bmpinfo, faces_tag, l_iter->f); + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); + } + } + } + + const int faces_len_direct = bmpinfo->faces_len; + + if (params->do_normals) { + /* - Extend to all faces vertices: + * Any changes to the faces normal needs to update all surrounding vertices. + * + * - Extend to all these vertices connected edges: + * These and needed to access those vertices edge vectors in normal calculation logic. + */ + + /* Vertices. */ + if (bmpinfo->verts == NULL) { + bmpinfo->verts_len_alloc = default_verts_len_alloc; + bmpinfo->verts = MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__); + verts_tag = BLI_BITMAP_NEW((size_t)bm->totvert, __func__); + } + + /* Edges. */ + if (bmpinfo->edges == NULL) { + bmpinfo->edges_len_alloc = default_edges_len_alloc; + bmpinfo->edges = MEM_mallocN((sizeof(BMEdge *) * bmpinfo->edges_len_alloc), __func__); + edges_tag = BLI_BITMAP_NEW((size_t)bm->totedge, __func__); + } + + for (int i = 0; i < bmpinfo->faces_len; i++) { + BMFace *f = bmpinfo->faces[i]; + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v)) { + continue; + } + BMVert *v = l_iter->v; + BMEdge *e_first = v->e; + BMEdge *e_iter = e_first; + do { + if (e_iter->l) { + partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter); + + /* These faces need to be taken into account when weighting vertex normals + * but aren't needed for tessellation nor do their normals need to be recalculated. + * These faces end up between `faces_len` and `faces_len_normal_calc_accumulate` + * in the faces array. */ + BMLoop *l_first_radial = e_iter->l; + BMLoop *l_iter_radial = l_first_radial; + /* Loop over radial loops. */ + do { + if (l_iter_radial->v == v) { + partial_elem_face_ensure(bmpinfo, faces_tag, l_iter_radial->f); + } + } while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial); + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); + } while ((l_iter = l_iter->next) != l_first); + } + } + + bmpinfo->faces_len_normal_calc_accumulate = bmpinfo->faces_len; + bmpinfo->faces_len = faces_len_direct; + + if (verts_tag) { + MEM_freeN(verts_tag); + } + if (edges_tag) { + MEM_freeN(edges_tag); + } + if (faces_tag) { + MEM_freeN(faces_tag); + } + + bmpinfo->params = *params; + + return bmpinfo; +} + +void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo) +{ + if (bmpinfo->verts) { + MEM_freeN(bmpinfo->verts); + } + if (bmpinfo->edges) { + MEM_freeN(bmpinfo->edges); + } + if (bmpinfo->faces) { + MEM_freeN(bmpinfo->faces); + } + MEM_freeN(bmpinfo); +} diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.h b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h new file mode 100644 index 00000000000..c0c9b275fa4 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h @@ -0,0 +1,69 @@ +/* + * 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 bmesh + */ + +#include "BLI_compiler_attrs.h" + +/** + * Parameters used to determine which kinds of data needs to be generated. + */ +typedef struct BMPartialUpdate_Params { + bool do_normals; + bool do_tessellate; +} BMPartialUpdate_Params; + +/** + * Cached data to speed up partial updates. + * + * Hints: + * + * - Avoid creating this data for single updates, + * it should be created and reused across multiple updates to gain a significant benefit + * (while transforming geometry for example). + * + * - Partial normal updates use face & loop indices, + * setting them to dirty values between updates will slow down normal recalculation. + */ +typedef struct BMPartialUpdate { + BMVert **verts; + BMEdge **edges; + BMFace **faces; + int verts_len, verts_len_alloc; + int edges_len, edges_len_alloc; + int faces_len, faces_len_alloc; + /** + * Faces at the end of `faces` that don't need to have the normals recalculated + * but must be included when waiting the vertex normals. + */ + int faces_len_normal_calc_accumulate; + + /** Store the parameters used in creation so invalid use can be asserted. */ + BMPartialUpdate_Params params; +} BMPartialUpdate; + +BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, + const BMPartialUpdate_Params *params, + const int verts_len, + bool (*filter_fn)(BMVert *, void *user_data), + void *user_data) + ATTR_NONNULL(1, 2, 4) ATTR_WARN_UNUSED_RESULT; + +void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo) ATTR_NONNULL(1); diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c index c8ea9429145..f2a5fbe3bde 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c @@ -32,6 +32,7 @@ #include "BLI_memarena.h" #include "BLI_polyfill_2d.h" #include "BLI_polyfill_2d_beautify.h" +#include "BLI_task.h" #include "bmesh.h" #include "bmesh_tools.h" @@ -151,6 +152,105 @@ void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Default Tessellation (Partial Updates) + * \{ */ + +struct PartialTessellationUserData { + BMFace **faces; + BMLoop *(*looptris)[3]; +}; + +struct PartialTessellationUserTLS { + MemArena *pf_arena; +}; + +static void mesh_calc_tessellation_for_face_partial_fn(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict tls) +{ + struct PartialTessellationUserTLS *tls_data = tls->userdata_chunk; + struct PartialTessellationUserData *data = userdata; + BMFace *f = data->faces[index]; + BMLoop *l = BM_FACE_FIRST_LOOP(f); + const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); + mesh_calc_tessellation_for_face(data->looptris + offset, f, &tls_data->pf_arena); +} + +static void mesh_calc_tessellation_for_face_partial_free_fn( + const void *__restrict UNUSED(userdata), void *__restrict tls_v) +{ + struct PartialTessellationUserTLS *tls_data = tls_v; + if (tls_data->pf_arena) { + BLI_memarena_free(tls_data->pf_arena); + } +} + +static void bm_mesh_calc_tessellation_with_partial__multi_threaded(BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo) +{ + const int faces_len = bmpinfo->faces_len; + BMFace **faces = bmpinfo->faces; + + struct PartialTessellationUserData data = { + .faces = faces, + .looptris = looptris, + }; + struct PartialTessellationUserTLS tls_dummy = {NULL}; + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = true; + settings.userdata_chunk = &tls_dummy; + settings.userdata_chunk_size = sizeof(tls_dummy); + settings.func_free = mesh_calc_tessellation_for_face_partial_free_fn; + + BLI_task_parallel_range( + 0, faces_len, &data, mesh_calc_tessellation_for_face_partial_fn, &settings); +} + +static void bm_mesh_calc_tessellation_with_partial__single_threaded(BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo) +{ + const int faces_len = bmpinfo->faces_len; + BMFace **faces = bmpinfo->faces; + + MemArena *pf_arena = NULL; + + for (int index = 0; index < faces_len; index++) { + BMFace *f = faces[index]; + BMLoop *l = BM_FACE_FIRST_LOOP(f); + const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2); + mesh_calc_tessellation_for_face(looptris + offset, f, &pf_arena); + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + } +} + +void BM_mesh_calc_tessellation_with_partial(BMesh *bm, + BMLoop *(*looptris)[3], + const BMPartialUpdate *bmpinfo) +{ + BLI_assert(bmpinfo->params.do_tessellate); + + BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE); + + /* On systems with 32+ cores, + * only a very small number of faces has any advantage single threading (in the 100's). + * Note that between 500-2000 quads, the difference isn't so much + * (tessellation isn't a bottleneck in this case anyway). + * Avoid the slight overhead of using threads in this case. */ + if (bmpinfo->faces_len < 1024) { + bm_mesh_calc_tessellation_with_partial__single_threaded(looptris, bmpinfo); + } + else { + bm_mesh_calc_tessellation_with_partial__multi_threaded(looptris, bmpinfo); + } +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Beauty Mesh Tessellation * diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h index 5606a5a7e02..f68a91cb988 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h @@ -20,5 +20,11 @@ * \ingroup bmesh */ +struct BMPartialUpdate; + void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]); void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]); + +void BM_mesh_calc_tessellation_with_partial(BMesh *bm, + BMLoop *(*looptris)[3], + const struct BMPartialUpdate *bmpinfo); diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index e7d5cb2f89d..2c32cd39002 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -21,6 +21,7 @@ */ struct Heap; +struct BMPartialUpdate; #include "BLI_compiler_attrs.h" diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index f03defe26e2..f0ced665679 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -46,6 +46,7 @@ * \{ */ struct ARegion; +struct BMPartialUpdate; struct Depsgraph; struct NumInput; struct Object; diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index f715228e25e..422370cb13b 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -52,6 +52,66 @@ /** \name Container TransCustomData Creation * \{ */ +static void tc_mesh_customdata_free_fn(struct TransInfo *t, + struct TransDataContainer *tc, + struct TransCustomData *custom_data); + +struct TransCustomDataLayer; +static void tc_mesh_customdatacorrect_free(struct TransCustomDataLayer *tcld); + +struct TransCustomDataMesh { + struct TransCustomDataLayer *cd_layer_correct; + struct { + struct BMPartialUpdate *cache; + + /** The size of proportional editing used for `partial_update_cache`. */ + float prop_size; + /** The size of proportional editing for the last update. */ + float prop_size_prev; + } partial_update; +}; + +static struct TransCustomDataMesh *tc_mesh_customdata_ensure(TransDataContainer *tc) +{ + struct TransCustomDataMesh *tcmd = tc->custom.type.data; + BLI_assert(tc->custom.type.data == NULL || + tc->custom.type.free_cb == tc_mesh_customdata_free_fn); + if (tc->custom.type.data == NULL) { + tc->custom.type.data = MEM_callocN(sizeof(struct TransCustomDataMesh), __func__); + tc->custom.type.free_cb = tc_mesh_customdata_free_fn; + tcmd = tc->custom.type.data; + } + return tcmd; +} + +static void tc_mesh_customdata_free(struct TransCustomDataMesh *tcmd) +{ + if (tcmd->cd_layer_correct != NULL) { + tc_mesh_customdatacorrect_free(tcmd->cd_layer_correct); + } + + if (tcmd->partial_update.cache != NULL) { + BM_mesh_partial_destroy(tcmd->partial_update.cache); + } + + MEM_freeN(tcmd); +} + +static void tc_mesh_customdata_free_fn(struct TransInfo *UNUSED(t), + struct TransDataContainer *UNUSED(tc), + struct TransCustomData *custom_data) +{ + struct TransCustomDataMesh *tcmd = custom_data->data; + tc_mesh_customdata_free(tcmd); + custom_data->data = NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData TransCustomDataLayer Creation + * \{ */ + struct TransCustomDataMergeGroup { /** map {BMVert: TransCustomDataLayerVert} */ struct LinkNode **cd_loop_groups; @@ -83,33 +143,6 @@ struct TransCustomDataLayer { bool use_merge_group; }; -static void tc_mesh_customdatacorrect_free_fn(struct TransInfo *UNUSED(t), - struct TransDataContainer *UNUSED(tc), - struct TransCustomData *custom_data) -{ - struct TransCustomDataLayer *tcld = custom_data->data; - bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - - if (tcld->bm_origfaces) { - BM_mesh_free(tcld->bm_origfaces); - } - if (tcld->origfaces) { - BLI_ghash_free(tcld->origfaces, NULL, NULL); - } - if (tcld->merge_group.origverts) { - BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); - } - if (tcld->arena) { - BLI_memarena_free(tcld->arena); - } - if (tcld->merge_group.customdatalayer_map) { - MEM_freeN(tcld->merge_group.customdatalayer_map); - } - - MEM_freeN(tcld); - custom_data->data = NULL; -} - #define USE_FACE_SUBSTITUTE #ifdef USE_FACE_SUBSTITUTE # define FACE_SUBSTITUTE_INDEX INT_MIN @@ -292,8 +325,8 @@ static void tc_mesh_customdatacorrect_init_container_merge_group(TransDataContai tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)); } -static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataContainer *tc, - const bool use_merge_group) +static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create_impl( + TransDataContainer *tc, const bool use_merge_group) { BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; @@ -341,18 +374,41 @@ static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataCo return tcld; } -static void tc_mesh_customdata_create(TransDataContainer *tc, const bool use_merge_group) +static void tc_mesh_customdatacorrect_create(TransDataContainer *tc, const bool use_merge_group) { struct TransCustomDataLayer *customdatacorrect; - customdatacorrect = tc_mesh_customdatacorrect_create(tc, use_merge_group); + customdatacorrect = tc_mesh_customdatacorrect_create_impl(tc, use_merge_group); if (!customdatacorrect) { return; } - BLI_assert(tc->custom.type.data == NULL); - tc->custom.type.data = customdatacorrect; - tc->custom.type.free_cb = tc_mesh_customdatacorrect_free_fn; + struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc); + BLI_assert(tcmd->cd_layer_correct == NULL); + tcmd->cd_layer_correct = customdatacorrect; +} + +static void tc_mesh_customdatacorrect_free(struct TransCustomDataLayer *tcld) +{ + bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + + if (tcld->bm_origfaces) { + BM_mesh_free(tcld->bm_origfaces); + } + if (tcld->origfaces) { + BLI_ghash_free(tcld->origfaces, NULL, NULL); + } + if (tcld->merge_group.origverts) { + BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); + } + if (tcld->arena) { + BLI_memarena_free(tcld->arena); + } + if (tcld->merge_group.customdatalayer_map) { + MEM_freeN(tcld->merge_group.customdatalayer_map); + } + + MEM_freeN(tcld); } void transform_convert_mesh_customdatacorrect_init(TransInfo *t) @@ -390,10 +446,14 @@ void transform_convert_mesh_customdatacorrect_init(TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (tc->custom.type.data != NULL) { - tc_mesh_customdatacorrect_free_fn(t, tc, &tc->custom.type); + struct TransCustomDataMesh *tcmd = tc->custom.type.data; + if (tcmd && tcmd->cd_layer_correct) { + tc_mesh_customdatacorrect_free(tcmd->cd_layer_correct); + tcmd->cd_layer_correct = NULL; + } } - tc_mesh_customdata_create(tc, use_merge_group); + tc_mesh_customdatacorrect_create(tc, use_merge_group); } } @@ -555,10 +615,11 @@ static void tc_mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tc static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final) { - if (!tc->custom.type.data) { + struct TransCustomDataMesh *tcmd = tc->custom.type.data; + struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL; + if (tcld == NULL) { return; } - struct TransCustomDataLayer *tcld = tc->custom.type.data; const bool use_merge_group = tcld->use_merge_group; struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data; @@ -590,7 +651,8 @@ static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_fina static void tc_mesh_customdatacorrect_restore(struct TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - struct TransCustomDataLayer *tcld = tc->custom.type.data; + struct TransCustomDataMesh *tcmd = tc->custom.type.data; + struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL; if (!tcld) { continue; } @@ -1617,6 +1679,100 @@ void createTransEditVerts(TransInfo *t) /** \name Recalc Mesh Data * \{ */ +static bool bm_vert_tag_filter_fn(BMVert *v, void *UNUSED(user_data)) +{ + return BM_elem_flag_test(v, BM_ELEM_TAG); +} + +static BMPartialUpdate *tc_mesh_ensure_partial_update(TransInfo *t, TransDataContainer *tc) +{ + struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc); + + if (tcmd->partial_update.cache) { + + /* Recalculate partial update data when the proportional editing size changes. + * + * Note that decreasing the proportional editing size requires the existing + * partial data is used before recreating this partial data at the smaller size. + * Since excluding geometry from being transformed requires an update. + * + * Extra logic is needed to account for this situation. */ + + bool recalc; + if (tcmd->partial_update.prop_size_prev < t->prop_size) { + /* Size increase, simply recalculate. */ + recalc = true; + } + else if (tcmd->partial_update.prop_size_prev > t->prop_size) { + /* Size decreased, first use this partial data since reducing the size will transform + * geometry which needs recalculating. */ + tcmd->partial_update.prop_size_prev = t->prop_size; + recalc = false; + } + else if (tcmd->partial_update.prop_size != t->prop_size) { + BLI_assert(tcmd->partial_update.prop_size > tcmd->partial_update.prop_size_prev); + recalc = true; + } + else { + BLI_assert(t->prop_size == tcmd->partial_update.prop_size_prev); + recalc = false; + } + + if (!recalc) { + return tcmd->partial_update.cache; + } + + BM_mesh_partial_destroy(tcmd->partial_update.cache); + tcmd->partial_update.cache = NULL; + } + + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + int verts_len = 0; + int i; + TransData *td; + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + if (td->factor != 0.0f) { + BMVert *v = (BMVert *)td->extra; + BM_elem_flag_enable(v, BM_ELEM_TAG); + verts_len += 1; + } + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) { + BMVert *v_mirr = (BMVert *)POINTER_OFFSET(td_mirror->loc_src, -offsetof(BMVert, co)); + + /* The equality check is to account for the case when topology mirror moves + * the vertex from it's original location to match it's symmetrical position, + * with proportional editing enabled. */ + if (BM_elem_flag_test(v_mirr, BM_ELEM_TAG) || !equals_v3v3(td_mirror->loc, td_mirror->iloc)) { + BMVert *v_mirr_other = (BMVert *)td_mirror->extra; + /* This assert should never fail since there is no overlap + * between mirrored vertices and non-mirrored. */ + BLI_assert(!BM_elem_flag_test(v_mirr_other, BM_ELEM_TAG)); + BM_elem_flag_enable(v_mirr_other, BM_ELEM_TAG); + verts_len += 1; + } + } + + tcmd->partial_update.cache = BM_mesh_partial_create_from_verts(em->bm, + &(BMPartialUpdate_Params){ + .do_tessellate = true, + .do_normals = true, + }, + verts_len, + bm_vert_tag_filter_fn, + NULL); + + tcmd->partial_update.prop_size_prev = t->prop_size; + tcmd->partial_update.prop_size = t->prop_size; + + return tcmd->partial_update.cache; +} + static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc) { if (tc->use_mirror_axis_any) { @@ -1678,8 +1834,22 @@ void recalcData_mesh(TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - EDBM_mesh_normals_update(em); - BKE_editmesh_looptri_calc(em); + + /* The additional cost of generating the partial connectivity data isn't justified + * when all data needs to be updated. + * + * While proportional editing can cause all geometry to need updating with a partial selection. + * It's impractical to calculate this ahead of time. + * Further, the down side of using partial updates when their not needed is negligible. */ + if (em->bm->totvert == em->bm->totvertsel) { + EDBM_mesh_normals_update(em); + BKE_editmesh_looptri_calc(em); + } + else { + BMPartialUpdate *partial_update_cache = tc_mesh_ensure_partial_update(t, tc); + BM_mesh_normals_update_with_partial(em->bm, partial_update_cache); + BKE_editmesh_looptri_calc_with_partial(em, partial_update_cache); + } } } /** \} */ -- cgit v1.2.3 From bfaf09b5bc97897eecf96cbc1b7aa46e6b38b4da Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Fri, 4 Jun 2021 17:22:31 -0600 Subject: Fix T88813: Scalable allocator not used on win10 Due to the way we ship the CRT on windows TBB's malloc proxy was unable to attach it self to the memory management functions on windows 10. This change moves ucrtbase.dll out of the blender.crt folder and back into the main blender folder to side step some undesirable behaviour on win10 making TBB once more able to attach it self. Having this work again, should give a speed boost in memory allocation heavy workloads such as mantaflow. For details on how this only failed on Win10 see T88813 --- build_files/cmake/platform/platform_win32_bundle_crt.cmake | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/build_files/cmake/platform/platform_win32_bundle_crt.cmake b/build_files/cmake/platform/platform_win32_bundle_crt.cmake index f5bc024e4e0..7b2e1edb1b3 100644 --- a/build_files/cmake/platform/platform_win32_bundle_crt.cmake +++ b/build_files/cmake/platform/platform_win32_bundle_crt.cmake @@ -15,6 +15,15 @@ if(WITH_WINDOWS_BUNDLE_CRT) include(InstallRequiredSystemLibraries) + # ucrtbase(d).dll cannot be in the manifest, due to the way windows 10 handles + # redirects for this dll, for details see T88813. + foreach(lib ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}) + string(FIND ${lib} "ucrtbase" pos) + if(NOT pos EQUAL -1) + list(REMOVE_ITEM CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${lib}) + install(FILES ${lib} DESTINATION . COMPONENT Libraries) + endif() + endforeach() # Install the CRT to the blender.crt Sub folder. install(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION ./blender.crt COMPONENT Libraries) -- cgit v1.2.3 From 14508ef100c9c6c67300ac480b32c88c0e134f25 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Sat, 5 Jun 2021 02:47:02 +0200 Subject: FFmpeg: Fix seeking not returning the correct frame when not using TC index Fixed the logic for seeking in ffmpeg video files. The main fix is that we now apply a small offset in ffmpeg_get_seek_pos to make sure we don't get the frame in front of the seek position when seeking backward. The rest of the changes is general cleanup and untangling code. Reviewed By: Richard Antalik Differential Revision: http://developer.blender.org/D11492 --- release/scripts/startup/bl_ui/space_sequencer.py | 1 - source/blender/imbuf/IMB_imbuf.h | 2 - source/blender/imbuf/intern/IMB_anim.h | 11 +- source/blender/imbuf/intern/anim_movie.c | 360 ++++++++++++----------- source/blender/makesdna/DNA_sequence_types.h | 2 +- source/blender/makesrna/intern/rna_sequencer.c | 6 - source/blender/sequencer/intern/render.c | 2 - source/blender/sequencer/intern/strip_add.c | 3 - 8 files changed, 195 insertions(+), 192 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index c5284f9911e..07d9b0ee1f8 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1381,7 +1381,6 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): col = layout.column() col.prop(strip, "filepath", text="") col.prop(strip.colorspace_settings, "name", text="Color Space") - col.prop(strip, "mpeg_preseek") col.prop(strip, "stream_index") col.prop(strip, "use_deinterlace") diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index d131e4dacdc..9c84127105a 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -380,8 +380,6 @@ bool IMB_anim_can_produce_frames(const struct anim *anim); */ int ismovie(const char *filepath); -void IMB_anim_set_preseek(struct anim *anim, int preseek); -int IMB_anim_get_preseek(struct anim *anim); int IMB_anim_get_image_width(struct anim *anim); int IMB_anim_get_image_height(struct anim *anim); diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index 7d7864306a1..cfeffcca0ea 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -87,7 +87,7 @@ struct anim_index; struct anim { int ib_flags; int curtype; - int curposition; /* index 0 = 1e, 1 = 2e, enz. */ + int cur_position; /* index 0 = 1e, 1 = 2e, enz. */ int duration_in_frames; int frs_sec; double frs_sec_base; @@ -105,7 +105,6 @@ struct anim { int orientation; size_t framesize; int interlacing; - int preseek; int streamindex; /* avi */ @@ -132,10 +131,10 @@ struct anim { struct SwsContext *img_convert_ctx; int videoStream; - struct ImBuf *last_frame; - int64_t last_pts; - int64_t next_pts; - AVPacket *next_packet; + struct ImBuf *cur_frame_final; + int64_t cur_pts; + int64_t cur_key_frame_pts; + AVPacket *cur_packet; #endif char index_dir[768]; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 5058bd2f637..622b6cbfc16 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -433,8 +433,7 @@ static int startavi(struct anim *anim) anim->orientation = 0; anim->framesize = anim->x * anim->y * 4; - anim->curposition = 0; - anim->preseek = 0; + anim->cur_position = 0; # if 0 printf("x:%d y:%d size:%d interl:%d dur:%d\n", @@ -650,12 +649,12 @@ static int startffmpeg(struct anim *anim) anim->orientation = 0; anim->framesize = anim->x * anim->y * 4; - anim->curposition = -1; - anim->last_frame = 0; - anim->last_pts = -1; - anim->next_pts = -1; - anim->next_packet = av_packet_alloc(); - anim->next_packet->stream_index = -1; + anim->cur_position = -1; + anim->cur_frame_final = 0; + anim->cur_pts = -1; + anim->cur_key_frame_pts = -1; + anim->cur_packet = av_packet_alloc(); + anim->cur_packet->stream_index = -1; anim->pFrame = av_frame_alloc(); anim->pFrameComplete = false; @@ -671,7 +670,7 @@ static int startffmpeg(struct anim *anim) fprintf(stderr, "Could not allocate frame data.\n"); avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -684,7 +683,7 @@ static int startffmpeg(struct anim *anim) fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n"); avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -706,13 +705,6 @@ static int startffmpeg(struct anim *anim) 1); } - if (pCodecCtx->has_b_frames) { - anim->preseek = 25; /* FIXME: detect gopsize ... */ - } - else { - anim->preseek = 0; - } - anim->img_convert_ctx = sws_getContext(anim->x, anim->y, anim->pCodecCtx->pix_fmt, @@ -728,7 +720,7 @@ static int startffmpeg(struct anim *anim) fprintf(stderr, "Can't transform color space??? Bailing out...\n"); avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -769,13 +761,13 @@ static int startffmpeg(struct anim *anim) /* postprocess the image in anim->pFrame and do color conversion * and deinterlacing stuff. * - * Output is anim->last_frame + * Output is anim->cur_frame_final */ static void ffmpeg_postprocess(struct anim *anim) { AVFrame *input = anim->pFrame; - ImBuf *ibuf = anim->last_frame; + ImBuf *ibuf = anim->cur_frame_final; int filter_y = 0; if (!anim->pFrameComplete) { @@ -902,7 +894,7 @@ static void ffmpeg_postprocess(struct anim *anim) } } -/* decode one video frame also considering the packet read into next_packet */ +/* decode one video frame also considering the packet read into cur_packet */ static int ffmpeg_decode_video_frame(struct anim *anim) { @@ -910,40 +902,43 @@ static int ffmpeg_decode_video_frame(struct anim *anim) av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n"); - if (anim->next_packet->stream_index == anim->videoStream) { - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; + if (anim->cur_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; } - while ((rval = av_read_frame(anim->pFormatCtx, anim->next_packet)) >= 0) { + while ((rval = av_read_frame(anim->pFormatCtx, anim->cur_packet)) >= 0) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n", - (anim->next_packet->stream_index == anim->videoStream) ? "->" : " ", - anim->next_packet->stream_index, + (anim->cur_packet->stream_index == anim->videoStream) ? "->" : " ", + anim->cur_packet->stream_index, anim->videoStream, - (anim->next_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->dts, - (anim->next_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->pts, - (anim->next_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); - if (anim->next_packet->stream_index == anim->videoStream) { + (anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->dts, + (anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts, + (anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); + if (anim->cur_packet->stream_index == anim->videoStream) { anim->pFrameComplete = 0; - avcodec_send_packet(anim->pCodecCtx, anim->next_packet); + avcodec_send_packet(anim->pCodecCtx, anim->cur_packet); anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + if (anim->pFrame->key_frame) { + anim->cur_key_frame_pts = anim->cur_pts; + } av_log(anim->pFormatCtx, AV_LOG_DEBUG, - " FRAME DONE: next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", + " FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (int64_t)anim->next_pts); + (int64_t)anim->cur_pts); break; } } - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; } if (rval == AVERROR_EOF) { @@ -954,20 +949,23 @@ static int ffmpeg_decode_video_frame(struct anim *anim) anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + if (anim->pFrame->key_frame) { + anim->cur_key_frame_pts = anim->cur_pts; + } av_log(anim->pFormatCtx, AV_LOG_DEBUG, - " FRAME DONE (after EOF): next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", + " FRAME DONE (after EOF): cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (int64_t)anim->next_pts); + (int64_t)anim->cur_pts); rval = 0; } } if (rval < 0) { - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; av_log(anim->pFormatCtx, AV_LOG_ERROR, @@ -1021,6 +1019,35 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx) return false; } +static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position) +{ + AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; + double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); + int64_t st_time = anim->pFormatCtx->start_time; + int64_t pos = (int64_t)(position)*AV_TIME_BASE; + /* Step back half a time base position to make sure that we get the requested + * frame and not the one after it. + */ + pos -= (AV_TIME_BASE / 2); + pos /= frame_rate; + + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n", + pos, + (st_time != AV_NOPTS_VALUE) ? st_time : 0); + + if (pos < 0) { + pos = 0; + } + + if (st_time != AV_NOPTS_VALUE) { + pos += st_time; + } + + return pos; +} + static int64_t ffmpeg_get_pts_to_search(struct anim *anim, struct anim_index *tc_index, int position) @@ -1045,77 +1072,60 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim, return pts_to_search; } +/* Check if the pts will get us the same frame that we already have in memory from last decode. */ static bool ffmpeg_pts_matches_last_frame(struct anim *anim, int64_t pts_to_search) { - return anim->last_frame && anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search; -} - -/* Requested video frame is expected to be found within different GOP as last decoded frame. - * Seeking to new position and scanning is fastest way to get requested frame. - * Check whether ffmpeg_can_scan() and ffmpeg_pts_matches_last_frame() is false before using this - * function. */ -static bool ffmpeg_can_seek(struct anim *anim, int position) -{ - return position != anim->curposition + 1; -} - -/* Requested video frame is expected to be found within same GOP as last decoded frame. - * Decoding frames in sequence until frame matches requested one is fastest way to get it. */ -static bool ffmpeg_can_scan(struct anim *anim, int position, struct anim_index *tc_index) -{ - if (position > anim->curposition + 1 && anim->preseek && !tc_index && - position - (anim->curposition + 1) < anim->preseek) { - return true; - } - - if (tc_index == NULL) { - return false; + if (anim->pFrame && anim->cur_frame_final) { + return labs(anim->cur_pts - pts_to_search) < anim->pFrame->pkt_duration; } - int new_frame_index = IMB_indexer_get_frame_index(tc_index, position); - int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->curposition); - return IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index); + return false; } static bool ffmpeg_is_first_frame_decode(struct anim *anim, int position) { - return position == 0 && anim->curposition == -1; + return position == 0 && anim->cur_position == -1; } /* Decode frames one by one until its PTS matches pts_to_search. */ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_search) { - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within preseek interval\n"); - - /* there seem to exist *very* silly GOP lengths out in the wild... */ - int count = 1000; + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within current GOP\n"); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n", - (int64_t)anim->next_pts, + (int64_t)anim->cur_pts, (int64_t)pts_to_search); - while (count > 0 && anim->next_pts < pts_to_search) { + int64_t start_gop_frame = anim->cur_key_frame_pts; + + while (anim->cur_pts < pts_to_search) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, " WHILE: pts=%" PRId64 " in search of %" PRId64 "\n", - (int64_t)anim->next_pts, + (int64_t)anim->cur_pts, (int64_t)pts_to_search); if (!ffmpeg_decode_video_frame(anim)) { break; } - count--; + + if (start_gop_frame != anim->cur_key_frame_pts) { + break; + } } - if (count == 0) { + + if (start_gop_frame != anim->cur_key_frame_pts) { + /* We went into an other GOP frame. This should never happen as we should have positioned us + * correctly by seeking into the GOP frame that contains the frame we want. */ av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN failed: completely lost in stream, " "bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n", - (int64_t)anim->next_pts, + (int64_t)anim->cur_pts, (int64_t)pts_to_search); } - if (anim->next_pts == pts_to_search) { + if (anim->cur_pts == pts_to_search) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n"); } else { @@ -1123,22 +1133,23 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea } } -/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or read_seek2() - * functions defined. When seeking in these formats, rule to seek to last necessary I-frame is not - * honored. It is not even guaranteed that I-frame, that must be decoded will be read. See - * https://trac.ffmpeg.org/ticket/1607 and https://developer.blender.org/T86944. */ -static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_pos) +/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or + * read_seek2() functions defined. When seeking in these formats, rule to seek to last + * necessary I-frame is not honored. It is not even guaranteed that I-frame, that must be + * decoded will be read. See https://trac.ffmpeg.org/ticket/1607 and + * https://developer.blender.org/T86944. */ +static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t *requested_pos) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - int64_t current_pos = requested_pos; + int64_t current_pos = *requested_pos; /* This time offset maximum limit is arbitrary. If some files fails to decode it may be - * increased. Seek performance will be negatively affected. Small initial offset is necessary - * because encoder can re-arrange frames as it needs but within it's delay, which is usually - * small. */ + * increased. Seek performance will be negatively affected. Small initial offset is + * necessary because encoder can re-arrange frames as it needs but within it's delay, which + * is usually small. */ for (int offset = 5; offset < 25; offset++) { - current_pos = requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate); + current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate); current_pos = max_ii(current_pos, 0); /* Seek to timestamp. */ @@ -1147,42 +1158,51 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_p } /* Read first video stream packet. */ - AVPacket read_packet = {0}; - while (av_read_frame(anim->pFormatCtx, &read_packet) >= 0) { - if (anim->next_packet->stream_index == anim->videoStream) { + AVPacket *read_packet = av_packet_alloc(); + while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) { + if (anim->cur_packet->stream_index == anim->videoStream) { break; } } /* If this packet contains I-frame, exit loop. This should be the frame that we need. */ - if (read_packet.flags & AV_PKT_FLAG_KEY) { + bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY; + av_packet_free(&read_packet); + if (is_key_frame) { break; } } + *requested_pos = current_pos; + /* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */ return av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD); } -/* Seek to last necessary I-frame and scan-decode until requested frame is found. */ -static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_index *tc_index) +/* Seek to last necessary key frame. */ +static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim_index *tc_index) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - int64_t st_time = anim->pFormatCtx->start_time; - - int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); int64_t pos; int ret; if (tc_index) { + /* We can use timestamps generated from our indexer to seek. */ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position); + int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->cur_position); + + if (IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) { + /* No need to seek, return early. */ + return 0; + } uint64_t dts; pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index); dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index); + anim->cur_key_frame_pts = pos; + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts); @@ -1198,22 +1218,9 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ } } else { - pos = (int64_t)(position)*AV_TIME_BASE / frame_rate; - - av_log(anim->pFormatCtx, - AV_LOG_DEBUG, - "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n", - pos, - (st_time != AV_NOPTS_VALUE) ? st_time : 0); - - if (pos < 0) { - pos = 0; - } - - if (st_time != AV_NOPTS_VALUE) { - pos += st_time; - } - + /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from. + */ + pos = ffmpeg_get_seek_pos(anim, position); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos); AVFormatContext *format_ctx = anim->pFormatCtx; @@ -1222,11 +1229,44 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); } else { - ret = ffmpeg_generic_seek_workaround(anim, pos); + ret = ffmpeg_generic_seek_workaround(anim, &pos); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos); + } + + if (ret >= 0) { + /* Double check if we need to seek and decode all packets. */ + AVPacket *current_gop_start_packet = av_packet_alloc(); + while (av_read_frame(anim->pFormatCtx, current_gop_start_packet) >= 0) { + if (current_gop_start_packet->stream_index == anim->videoStream) { + break; + } + av_packet_unref(current_gop_start_packet); + } + bool same_gop = current_gop_start_packet->pts == anim->cur_key_frame_pts; + + if (same_gop && position > anim->cur_position) { + /* Change back to our old frame position so we can simply continue decoding from there. */ + AVPacket *temp = av_packet_alloc(); + while (av_read_frame(anim->pFormatCtx, temp) >= 0) { + if (temp->stream_index == anim->videoStream && temp->pts == anim->cur_packet->pts) { + break; + } + av_packet_unref(temp); + } + av_packet_free(¤t_gop_start_packet); + av_packet_free(&temp); + return 0; + } + + anim->cur_key_frame_pts = current_gop_start_packet->pts; + av_packet_free(¤t_gop_start_packet); + /* Seek back so we are at the correct position after we decoded a frame. */ + av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); } } if (ret < 0) { + int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); av_log(anim->pFormatCtx, AV_LOG_ERROR, "FETCH: " @@ -1234,25 +1274,19 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ "): errcode = %d\n", pos, position, - (int64_t)pts_to_search, + pts_to_search, ret); } avcodec_flush_buffers(anim->pCodecCtx); - anim->next_pts = -1; + anim->cur_pts = -1; - if (anim->next_packet->stream_index == anim->videoStream) { - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; - } - - /* memset(anim->pFrame, ...) ?? */ - if (ret < 0) { - /* Seek failed. */ - return; + if (anim->cur_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; } - ffmpeg_decode_video_frame_scan(anim, pts_to_search); + return ret; } static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc) @@ -1282,25 +1316,22 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, - "FETCH: frame repeat: last: %" PRId64 " next: %" PRId64 "\n", - (int64_t)anim->last_pts, - (int64_t)anim->next_pts); - IMB_refImBuf(anim->last_frame); - anim->curposition = position; - return anim->last_frame; + "FETCH: frame repeat: pts: %" PRId64 "\n", + (int64_t)anim->cur_pts); + IMB_refImBuf(anim->cur_frame_final); + anim->cur_position = position; + return anim->cur_frame_final; } - if (ffmpeg_can_scan(anim, position, tc_index) || ffmpeg_is_first_frame_decode(anim, position)) { - ffmpeg_decode_video_frame_scan(anim, pts_to_search); - } - else if (ffmpeg_can_seek(anim, position)) { - ffmpeg_seek_and_decode(anim, position, tc_index); - } - else { + if (position == anim->cur_position + 1 || ffmpeg_is_first_frame_decode(anim, position)) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: no seek necessary, just continue...\n"); + ffmpeg_decode_video_frame(anim); + } + else if (ffmpeg_seek_to_key_frame(anim, position, tc_index) >= 0) { + ffmpeg_decode_video_frame_scan(anim, pts_to_search); } - IMB_freeImBuf(anim->last_frame); + IMB_freeImBuf(anim->cur_frame_final); /* Certain versions of FFmpeg have a bug in libswscale which ends up in crash * when destination buffer is not properly aligned. For example, this happens @@ -1320,23 +1351,20 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ * * The issue was reported to FFmpeg under ticket #8747 in the FFmpeg tracker * and is fixed in the newer versions than 4.3.1. */ - anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, 0); - anim->last_frame->rect = MEM_mallocN_aligned((size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf"); - anim->last_frame->mall |= IB_rect; + anim->cur_frame_final = IMB_allocImBuf(anim->x, anim->y, 32, 0); + anim->cur_frame_final->rect = MEM_mallocN_aligned( + (size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf"); + anim->cur_frame_final->mall |= IB_rect; - anim->last_frame->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace); + anim->cur_frame_final->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace); ffmpeg_postprocess(anim); - anim->last_pts = anim->next_pts; - - ffmpeg_decode_video_frame(anim); + anim->cur_position = position; - anim->curposition = position; + IMB_refImBuf(anim->cur_frame_final); - IMB_refImBuf(anim->last_frame); - - return anim->last_frame; + return anim->cur_frame_final; } static void free_anim_ffmpeg(struct anim *anim) @@ -1348,7 +1376,7 @@ static void free_anim_ffmpeg(struct anim *anim) if (anim->pCodecCtx) { avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrame); @@ -1369,7 +1397,7 @@ static void free_anim_ffmpeg(struct anim *anim) av_frame_free(&anim->pFrameDeinterlaced); sws_freeContext(anim->img_convert_ctx); - IMB_freeImBuf(anim->last_frame); + IMB_freeImBuf(anim->cur_frame_final); } anim->duration_in_frames = 0; } @@ -1503,13 +1531,13 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, an_stringenc(anim->name, head, tail, digits, pic); ibuf = IMB_loadiffname(anim->name, IB_rect, anim->colorspace); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; } break; case ANIM_MOVIE: ibuf = movie_fetchibuf(anim, position); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; IMB_convert_rgba_to_abgr(ibuf); } break; @@ -1517,7 +1545,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, case ANIM_AVI: ibuf = avi_fetchibuf(anim, position); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; } break; #endif @@ -1525,7 +1553,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, case ANIM_FFMPEG: ibuf = ffmpeg_fetchibuf(anim, position, tc); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; } filter_y = 0; /* done internally */ break; @@ -1536,7 +1564,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, if (filter_y) { IMB_filtery(ibuf); } - BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->curposition + 1); + BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->cur_position + 1); } return ibuf; } @@ -1591,16 +1619,6 @@ bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bo return false; } -void IMB_anim_set_preseek(struct anim *anim, int preseek) -{ - anim->preseek = preseek; -} - -int IMB_anim_get_preseek(struct anim *anim) -{ - return anim->preseek; -} - int IMB_anim_get_image_width(struct anim *anim) { return anim->x; diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index f59f51ea28a..7e0bf81457d 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -173,7 +173,7 @@ typedef struct Sequence { float sat; float mul, handsize; - short anim_preseek; + short anim_preseek; /* UNUSED. */ /** Streamindex for movie or sound files with several streams. */ short streamindex; /** For multicam source selection. */ diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 4dba82443d6..41b60bed5bb 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2417,12 +2417,6 @@ static void rna_def_movie(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Movie Sequence", "Sequence strip to load a video"); RNA_def_struct_sdna(srna, "Sequence"); - prop = RNA_def_property(srna, "mpeg_preseek", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "anim_preseek"); - RNA_def_property_range(prop, 0, 50); - RNA_def_property_ui_text(prop, "MPEG Preseek", "For MPEG movies, preseek this many frames"); - RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); - prop = RNA_def_property(srna, "stream_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "streamindex"); RNA_def_property_range(prop, 0, 20); diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 4b8df111f07..0bad66c3d7a 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1125,8 +1125,6 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context, ImBuf *ibuf = NULL; IMB_Proxy_Size psize = SEQ_rendersize_to_proxysize(context->preview_render_size); - IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); - if (SEQ_can_use_proxy(context, seq, psize)) { /* Try to get a proxy image. * Movie proxies are handled by ImBuf module with exception of `custom file` setting. */ diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 64671aeb265..9e5afb45f2a 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -551,7 +551,6 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL float video_fps = 0.0f; if (anim_arr[0] != NULL) { - seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); IMB_anim_load_metadata(anim_arr[0]); @@ -702,8 +701,6 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo seq->len = IMB_anim_get_duration( sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN); - seq->anim_preseek = IMB_anim_get_preseek(sanim->anim); - seq->len -= seq->anim_startofs; seq->len -= seq->anim_endofs; if (seq->len < 0) { -- cgit v1.2.3 From 022f8b552d7c8efe39527ec49a593a706fa0c8fc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 5 Jun 2021 15:03:09 +1000 Subject: Cleanup: spelling in comments Also remove reference to function that never existed for adding `bNode`. --- intern/ghost/intern/GHOST_SystemWayland.cpp | 2 +- source/blender/blenkernel/intern/collection.c | 2 +- source/blender/blenlib/intern/memory_utils.c | 2 +- source/blender/editors/space_node/node_edit.cc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 1d2a349fdf1..83b9b2ba36b 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -1729,7 +1729,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) cursor_t *c = &input->cursor; if (!c->theme) { - /* The cursor surface hasn't entered an output yet. Initialise theme with scale 1. */ + /* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */ c->theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->inputs[0]->system->shm()); } diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 5a7fe71de8e..7b1aaf04640 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1643,7 +1643,7 @@ void BKE_collection_parent_relations_rebuild(Collection *collection) continue; } - /* Can happen when remaping data partially out-of-Main (during advanced ID management + /* Can happen when remapping data partially out-of-Main (during advanced ID management * operations like liboverride resync e.g.). */ if ((child->collection->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)) != 0) { continue; diff --git a/source/blender/blenlib/intern/memory_utils.c b/source/blender/blenlib/intern/memory_utils.c index d477c20a242..5ca7b96c136 100644 --- a/source/blender/blenlib/intern/memory_utils.c +++ b/source/blender/blenlib/intern/memory_utils.c @@ -31,7 +31,7 @@ #include "BLI_strict_flags.h" /** - * Check if memory is zero'd, as with memset(arr, 0, arr_size) + * Check if memory is zeroed, as with `memset(arr, 0, arr_size)`. */ bool BLI_memory_is_zero(const void *arr, const size_t arr_size) { diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 90df7e80c38..d86b069aac3 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -753,7 +753,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti ED_node_tag_update_nodetree(bmain, ntree, node); } - /* addnode() doesn't link this yet... */ + /* Adding a node doesn't link this yet. */ node->id = (ID *)BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); } else if (node->type == CMP_NODE_COMPOSITE) { -- cgit v1.2.3 From a5114bfb85eb0a3ec2fd86de4f12b222bdfe11e6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 5 Jun 2021 15:06:47 +1000 Subject: Cleanup: indentation --- release/scripts/presets/keyconfig/keymap_data/blender_default.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 841f9721111..fe034686294 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1081,8 +1081,8 @@ def km_view3d(params): op_menu_pie("VIEW3D_MT_view_pie", {"type": 'D', "value": 'CLICK_DRAG'}), # Numpad views. ("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None), - ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS'}, - {"properties": [("type", 'FRONT')]}), + ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS'}, + {"properties": [("type", 'FRONT')]}), ("view3d.view_orbit", {"type": 'NUMPAD_2', "value": 'PRESS', "repeat": True}, {"properties": [("type", 'ORBITDOWN')]}), ("view3d.view_axis", {"type": 'NUMPAD_3', "value": 'PRESS'}, @@ -2591,7 +2591,7 @@ def km_sequencer(params): ("sequencer.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True, "shift": True}, - {"properties": [("keep_offset", True)]}), + {"properties": [("keep_offset", True)]}), ("sequencer.images_separate", {"type": 'Y', "value": 'PRESS'}, None), ("sequencer.meta_toggle", {"type": 'TAB', "value": 'PRESS'}, None), ("sequencer.meta_make", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), -- cgit v1.2.3 From ca3891e83ba6156c18a8debd4008290429a51538 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 5 Jun 2021 15:06:48 +1000 Subject: Fix T88828: View/Navigation(Walk/Fly) disappeared from keymap This was unintentionally removed in f92f5d1ac62c66ceb7a6ac1ff69084fbd5e3614a. --- release/scripts/presets/keyconfig/keymap_data/blender_default.py | 1 + 1 file changed, 1 insertion(+) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index fe034686294..dbc7ce650a3 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1079,6 +1079,7 @@ def km_view3d(params): ("view3d.view_all", {"type": 'C', "value": 'PRESS', "shift": True}, {"properties": [("center", True)]}), op_menu_pie("VIEW3D_MT_view_pie", {"type": 'D', "value": 'CLICK_DRAG'}), + ("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None), # Numpad views. ("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None), ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS'}, -- cgit v1.2.3 From c7fee64dea3a2ae99b0ca239d047678d3da39fab Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 5 Jun 2021 17:05:11 +1000 Subject: Cleanup: use ternary operator for icon argument --- .../scripts/startup/bl_ui/space_view3d_toolbar.py | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 4ae3310e510..604509b99f9 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1456,11 +1456,12 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): elif brush.gpencil_tool == 'FILL': row = col.row(align=True) row.prop(gp_settings, "fill_draw_mode", text="Boundary") - if gp_settings.show_fill_boundary: - icon = 'HIDE_OFF' - else: - icon = 'HIDE_ON' - row.prop(gp_settings, "show_fill_boundary", text="", icon=icon) + row.prop( + gp_settings, + "show_fill_boundary", + icon='HIDE_OFF' if gp_settings.show_fill_boundary else 'HIDE_ON', + text="", + ) col.separator() row = col.row(align=True) @@ -1469,12 +1470,12 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): col.separator() row = col.row(align=True) row.prop(gp_settings, "extend_stroke_factor") - if gp_settings.show_fill_extend: - icon = 'HIDE_OFF' - else: - icon = 'HIDE_ON' - - row.prop(gp_settings, "show_fill_extend", text="", icon=icon) + row.prop( + gp_settings, + "show_fill_extend", + icon='HIDE_OFF' if gp_settings.show_fill_extend else 'HIDE_ON', + text="", + ) col.separator() col.prop(gp_settings, "fill_leak", text="Leak Size") -- cgit v1.2.3 From 3c9c557580e86f592b861319e9c083be0d754ecf Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 5 Jun 2021 17:13:12 +1000 Subject: Fix assert in gpencil_batches_ensure --- source/blender/draw/intern/draw_cache_impl_gpencil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 49b5e0fecd3..bea9ba1122b 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -405,7 +405,7 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr if (cache->vbo == NULL) { /* Should be discarded together. */ BLI_assert(cache->vbo == NULL && cache->ibo == NULL); - BLI_assert(cache->stroke_batch == NULL && cache->stroke_batch == NULL); + BLI_assert(cache->fill_batch == NULL && cache->stroke_batch == NULL); /* TODO/PERF: Could be changed to only do it if needed. * For now it's simpler to assume we always need it * since multiple viewport could or could not need it. -- cgit v1.2.3 From bcf9c73cbc8386bdef1e08af73e66d0e5c57ad6e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 5 Jun 2021 17:16:37 +1000 Subject: Fix assert check in BLI_polyfill_beautify --- source/blender/blenlib/intern/polyfill_2d_beautify.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/intern/polyfill_2d_beautify.c b/source/blender/blenlib/intern/polyfill_2d_beautify.c index 7bfca149ffb..98fa5c872b0 100644 --- a/source/blender/blenlib/intern/polyfill_2d_beautify.c +++ b/source/blender/blenlib/intern/polyfill_2d_beautify.c @@ -375,7 +375,7 @@ void BLI_polyfill_beautify(const float (*coords)[2], for (uint i = 0, base_index = 0; i < order_edges_len; base_index++) { const struct OrderEdge *oe_a = &order_edges[i++]; const struct OrderEdge *oe_b = &order_edges[i++]; - BLI_assert(oe_a->verts[0] == oe_a->verts[0] && oe_a->verts[1] == oe_a->verts[1]); + BLI_assert(oe_a->verts[0] == oe_b->verts[0] && oe_a->verts[1] == oe_b->verts[1]); half_edges[oe_a->e_half].e_radial = oe_b->e_half; half_edges[oe_b->e_half].e_radial = oe_a->e_half; half_edges[oe_a->e_half].base_index = base_index; -- cgit v1.2.3 From 1a912462f4e51d069bda5084f3d702006e0a5b7e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 5 Jun 2021 21:28:16 +1000 Subject: BMesh: avoid extra faces-of-edges loop building partial update data --- source/blender/bmesh/intern/bmesh_mesh_partial_update.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c index b87d9811049..2290e58fe6c 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c +++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c @@ -219,7 +219,9 @@ BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm, BMEdge *e_iter = e_first; do { if (e_iter->l) { - partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter); + if (!partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter)) { + continue; + } /* These faces need to be taken into account when weighting vertex normals * but aren't needed for tessellation nor do their normals need to be recalculated. -- cgit v1.2.3 From 2cd1bc3aa7e66a295313c9ab82fbd7ba4b4905e1 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Sat, 5 Jun 2021 09:46:00 -0300 Subject: Fix T88859: Assert when changing view modes The `loose_lines`' ibo was not being initialized. --- .../blender/draw/intern/draw_cache_extract_mesh.cc | 21 +++++++++--- .../intern/draw_cache_extract_mesh_extractors.c | 38 ++++++++++++++-------- .../draw/intern/draw_cache_extract_mesh_private.h | 4 +-- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index b48ee1ddc3f..fca40206659 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -777,7 +777,6 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, * +-----> | extract_task2_loop_3 | * +----------------------+ */ - const bool do_lines_loose_subbuffer = mbc->ibo.lines_loose != nullptr; const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 || GPU_use_hq_normals_workaround(); @@ -787,8 +786,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, #define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \ do { \ if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \ - const MeshExtract *extractor = mesh_extract_override_get( \ - &extract_##name, do_hq_normals, do_lines_loose_subbuffer); \ + const MeshExtract *extractor = mesh_extract_override_get(&extract_##name, do_hq_normals); \ extractors.append(extractor); \ } \ } while (0) @@ -818,7 +816,22 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots); EXTRACT_ADD_REQUESTED(IBO, ibo, tris); - EXTRACT_ADD_REQUESTED(IBO, ibo, lines); + if (DRW_ibo_requested(mbc->ibo.lines)) { + const MeshExtract *extractor; + if (mbc->ibo.lines_loose != nullptr) { + /* Update #lines_loose ibo. */ + extractor = &extract_lines_with_lines_loose; + } + else { + extractor = &extract_lines; + } + extractors.append(extractor); + } + else if (DRW_ibo_requested(mbc->ibo.lines_loose)) { + /* Note: #ibo.lines must have been created first. */ + const MeshExtract *extractor = &extract_lines_loose_only; + extractors.append(extractor); + } EXTRACT_ADD_REQUESTED(IBO, ibo, points); EXTRACT_ADD_REQUESTED(IBO, ibo, fdots); EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c index a806632c18d..0a3945291bb 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c @@ -94,24 +94,12 @@ static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *ex return extractor; } -static const MeshExtract *mesh_extract_override_loose_lines(const MeshExtract *extractor) -{ - if (extractor == &extract_lines) { - return &extract_lines_with_lines_loose; - } - return extractor; -} - const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, - const bool do_hq_normals, - const bool do_lines_loose_subbuffer) + const bool do_hq_normals) { if (do_hq_normals) { extractor = mesh_extract_override_hq_normals(extractor); } - if (do_lines_loose_subbuffer) { - extractor = mesh_extract_override_loose_lines(extractor); - } return extractor; } @@ -386,7 +374,7 @@ const MeshExtract extract_lines = { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Extract Loose Edges Sub Buffer +/** \name Extract Lines and Loose Edges Sub Buffer * \{ */ static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache) @@ -424,6 +412,28 @@ const MeshExtract extract_lines_with_lines_loose = { /** \} */ +/* ---------------------------------------------------------------------- */ +/** \name Extract Loose Edges Sub Buffer + * \{ */ + +static void *extract_lines_loose_only_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf) +{ + BLI_assert(buf == cache->final.ibo.lines_loose); + UNUSED_VARS_NDEBUG(buf); + extract_lines_loose_subbuffer(mr, cache); + return NULL; +} + +const MeshExtract extract_lines_loose_only = { + .init = extract_lines_loose_only_init, + .data_type = 0, + .use_threading = false, + .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose)}; + +/** \} */ + /* ---------------------------------------------------------------------- */ /** \name Extract Point Indices * \{ */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index 644354989d8..6761282fe79 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -463,8 +463,7 @@ void mesh_render_data_update_looptris(MeshRenderData *mr, void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc); eMRIterType mesh_extract_iter_type(const MeshExtract *ext); const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, - const bool do_hq_normals, - const bool do_lines_loose_subbuffer); + const bool do_hq_normals); /* * Total number of extractions types. */ @@ -473,6 +472,7 @@ const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor, extern const MeshExtract extract_tris; extern const MeshExtract extract_lines; extern const MeshExtract extract_lines_with_lines_loose; +extern const MeshExtract extract_lines_loose_only; extern const MeshExtract extract_points; extern const MeshExtract extract_fdots; extern const MeshExtract extract_lines_paint_mask; -- cgit v1.2.3 From d2dc452333a4cfd335a0075277c21349473ba678 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 22 Nov 2020 14:17:21 +0300 Subject: Limit Rotation: add an Euler Order option. Since Limit Rotation is based on Euler decomposition, it should allow specifying the order to use for the same reasons as Copy Rotation does, namely, if the bone uses Quaternion rotation for its animation channels, there is no way to choose the order for the constraint. Ref D9626 --- release/scripts/startup/bl_ui/properties_constraint.py | 1 + source/blender/blenkernel/intern/constraint.c | 12 ++++++++++-- source/blender/makesdna/DNA_constraint_types.h | 2 ++ source/blender/makesrna/intern/rna_constraint.c | 6 ++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index e835e577953..a88def34767 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -245,6 +245,7 @@ class ConstraintButtonsPanel: sub.prop(con, "max_z", text="Max") row.label(icon='BLANK1') + layout.prop(con, "euler_order", text="Order") layout.prop(con, "use_transform_limit") self.space_template(layout, con, target=False, owner=True) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 9293a2b449a..766d0f5dd2e 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -1635,10 +1635,18 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN float eul[3]; float size[3]; + /* Select the Euler rotation order, defaulting to the owner value. */ + short rot_order = cob->rotOrder; + + if (data->euler_order != CONSTRAINT_EULER_AUTO) { + rot_order = data->euler_order; + } + + /* Decompose the matrix using the specified order. */ copy_v3_v3(loc, cob->matrix[3]); mat4_to_size(size, cob->matrix); - mat4_to_eulO(eul, cob->rotOrder, cob->matrix); + mat4_to_eulO(eul, rot_order, cob->matrix); /* constraint data uses radians internally */ @@ -1671,7 +1679,7 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN } } - loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, cob->rotOrder); + loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, rot_order); } static bConstraintTypeInfo CTI_ROTLIMIT = { diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 07fbf263d80..a94bc4625df 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -534,6 +534,8 @@ typedef struct bRotLimitConstraint { float zmin, zmax; short flag; short flag2; + char euler_order; + char _pad[3]; } bRotLimitConstraint; /* Limit Scale Constraint */ diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index a3934b12a44..b363dcd4ba9 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -2598,6 +2598,12 @@ static void rna_def_constraint_rotation_limit(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Maximum Z", "Highest Z value to allow"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + prop = RNA_def_property(srna, "euler_order", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "euler_order"); + RNA_def_property_enum_items(prop, euler_order_items); + RNA_def_property_ui_text(prop, "Euler Order", "Explicitly specify the euler rotation order"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM); RNA_def_property_ui_text( -- cgit v1.2.3 From edaaa2afddb2132e56f39791e559b084b6df8773 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 5 Jun 2021 15:47:11 +0300 Subject: Limit Rotation: explicitly orthogonalize the matrix before processing. Add a call to orthogonalize the matrix before processing for the same reasons as D8915, and an early exit in case no limits are enabled for a bit of extra efficiency. Since the constraint goes through Euler decomposition, it would in fact remove shear even before this change, but the resulting rotation won't make much sense. This change allows using the constraint without any enabled limits purely for the purpose of efficiently removing shear. Differential Revision: https://developer.blender.org/D9626 --- source/blender/blenkernel/intern/constraint.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 766d0f5dd2e..826c79c3764 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -1635,6 +1635,16 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN float eul[3]; float size[3]; + /* This constraint is based on euler rotation math, which doesn't work well with shear. + * The Y axis is chosen as the main one because constraints are most commonly used on bones. + * This also allows using the constraint to simply remove shear. */ + orthogonalize_m4_stable(cob->matrix, 1, false); + + /* Only do the complex processing if some limits are actually enabled. */ + if (!(data->flag & (LIMIT_XROT | LIMIT_YROT | LIMIT_ZROT))) { + return; + } + /* Select the Euler rotation order, defaulting to the owner value. */ short rot_order = cob->rotOrder; -- cgit v1.2.3