From 4cd12453962fcb77adb79ab27144fa8c18a24bc3 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Thu, 4 Jul 2024 13:42:23 +0200 Subject: [PATCH] Compositor: Support viewport in Cryptomatte picker This patch supports the Cryptomatte picker in the 3D viewport. Instead of picking a color from the viewport GPU texture, we instead sample the scene directly to get the object or material under the cursor, then hash their names to get the Cryptomatte hash value. We do this because the viewport texture have limited precision, so it can't store the Cryptomatte hash values. Additionally, we adjust the Cryptomatte session code to extract the Cryptomatte manifest from the scene directly, as opposed to the RenderResult. This is done to make it work even when no RenderResult exist, as is the case for the viewport compositor, which is needed especially after #123378. Pull Request: https://projects.blender.org/blender/blender/pulls/123815 --- source/blender/blenkernel/BKE_cryptomatte.h | 6 +- .../blender/blenkernel/intern/cryptomatte.cc | 54 +++++++++++++--- .../interface/eyedroppers/eyedropper_color.cc | 62 ++++++++++++++++++- .../nodes/node_composite_cryptomatte.cc | 24 ++----- 4 files changed, 115 insertions(+), 31 deletions(-) diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h index 8eca69d1217..c6efce54a3c 100644 --- a/source/blender/blenkernel/BKE_cryptomatte.h +++ b/source/blender/blenkernel/BKE_cryptomatte.h @@ -25,7 +25,11 @@ struct Scene; struct CryptomatteSession *BKE_cryptomatte_init(void); struct CryptomatteSession *BKE_cryptomatte_init_from_render_result( const struct RenderResult *render_result); -struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *scene); +/* Initializes a cryptomatte session from the view layers of the given scene. If build_meta_data is + * true, the object and material IDs in the view layer will be hashed and added to the Cryptomatte + * layers, allowing hash-name lookups. */ +struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *scene, + bool build_meta_data); struct CryptomatteSession *BKE_cryptomatte_init_from_view_layer( const struct ViewLayer *view_layer); void BKE_cryptomatte_free(struct CryptomatteSession *session); diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc index a8d7e97cc2f..66a5baa2266 100644 --- a/source/blender/blenkernel/intern/cryptomatte.cc +++ b/source/blender/blenkernel/intern/cryptomatte.cc @@ -9,7 +9,9 @@ #include "BKE_cryptomatte.h" #include "BKE_cryptomatte.hh" #include "BKE_image.h" +#include "BKE_layer.hh" #include "BKE_main.hh" +#include "BKE_material.h" #include "DNA_layer_types.h" #include "DNA_material_types.h" @@ -43,8 +45,8 @@ struct CryptomatteSession { CryptomatteSession(const Main *bmain); CryptomatteSession(StampData *stamp_data); CryptomatteSession(const ViewLayer *view_layer); - CryptomatteSession(const Scene *scene); - void init(const ViewLayer *view_layer); + CryptomatteSession(const Scene *scene, bool build_meta_data = false); + void init(const ViewLayer *view_layer, bool build_meta_data = false); blender::bke::cryptomatte::CryptomatteLayer &add_layer(std::string layer_name); std::optional operator[](float encoded_hash) const; @@ -93,14 +95,19 @@ CryptomatteSession::CryptomatteSession(const ViewLayer *view_layer) init(view_layer); } -CryptomatteSession::CryptomatteSession(const Scene *scene) +CryptomatteSession::CryptomatteSession(const Scene *scene, bool build_meta_data) { + + if (build_meta_data) { + BKE_scene_view_layers_synced_ensure(scene); + } + LISTBASE_FOREACH (const ViewLayer *, view_layer, &scene->view_layers) { - init(view_layer); + init(view_layer, build_meta_data); } } -void CryptomatteSession::init(const ViewLayer *view_layer) +void CryptomatteSession::init(const ViewLayer *view_layer, bool build_meta_data) { eViewLayerCryptomatteFlags cryptoflags = static_cast( view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL); @@ -108,14 +115,37 @@ void CryptomatteSession::init(const ViewLayer *view_layer) cryptoflags = static_cast(VIEW_LAYER_CRYPTOMATTE_ALL); } + ListBase *object_bases = BKE_view_layer_object_bases_get(const_cast(view_layer)); + if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) { - add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_OBJECT); + blender::bke::cryptomatte::CryptomatteLayer &objects = add_layer( + blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_OBJECT); + + if (build_meta_data) { + LISTBASE_FOREACH (Base *, base, object_bases) { + objects.add_ID(base->object->id); + } + } } + if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_ASSET) { add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_ASSET); } + if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_MATERIAL) { - add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_MATERIAL); + blender::bke::cryptomatte::CryptomatteLayer &materials = add_layer( + blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_MATERIAL); + + if (build_meta_data) { + LISTBASE_FOREACH (Base *, base, object_bases) { + for (int i = 0; i < base->object->totcol; i++) { + Material *material = BKE_object_material_get(base->object, i + 1); + if (material) { + materials.add_ID(material->id); + } + } + } + } } } @@ -144,15 +174,21 @@ CryptomatteSession *BKE_cryptomatte_init() return session; } +CryptomatteSession *BKE_cryptomatte_init_from_main(const Main *bmain) +{ + CryptomatteSession *session = new CryptomatteSession(bmain); + return session; +} + CryptomatteSession *BKE_cryptomatte_init_from_render_result(const RenderResult *render_result) { CryptomatteSession *session = new CryptomatteSession(render_result->stamp_data); return session; } -CryptomatteSession *BKE_cryptomatte_init_from_scene(const Scene *scene) +CryptomatteSession *BKE_cryptomatte_init_from_scene(const Scene *scene, bool build_meta_data) { - CryptomatteSession *session = new CryptomatteSession(scene); + CryptomatteSession *session = new CryptomatteSession(scene, build_meta_data); return session; } diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_color.cc b/source/blender/editors/interface/eyedroppers/eyedropper_color.cc index 07e78b4279c..1fc48061c07 100644 --- a/source/blender/editors/interface/eyedroppers/eyedropper_color.cc +++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.cc @@ -13,16 +13,20 @@ #include "MEM_guardedalloc.h" +#include "DNA_material_types.h" +#include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_string.h" +#include "BLI_string_ref.hh" #include "BKE_context.hh" #include "BKE_cryptomatte.h" #include "BKE_image.h" +#include "BKE_material.h" #include "BKE_report.hh" #include "BKE_screen.hh" @@ -174,6 +178,40 @@ static void eyedropper_exit(bContext *C, wmOperator *op) /* *** eyedropper_color_ helper functions *** */ +static bool eyedropper_cryptomatte_sample_view3d_fl(bContext *C, + const char *type_name, + const int mval[2], + float r_col[3]) +{ + int material_slot = 0; + Object *object = ED_view3d_give_material_slot_under_cursor(C, mval, &material_slot); + if (!object) { + return false; + } + + const ID *id = nullptr; + if (blender::StringRef(type_name).endswith(RE_PASSNAME_CRYPTOMATTE_OBJECT)) { + id = &object->id; + } + else if (blender::StringRef(type_name).endswith(RE_PASSNAME_CRYPTOMATTE_MATERIAL)) { + Material *material = BKE_object_material_get(object, material_slot); + if (!material) { + return false; + } + id = &material->id; + } + + if (!id) { + return false; + } + + const char *name = &id->name[2]; + const int name_length = BLI_strnlen(name, MAX_NAME - 2); + uint32_t cryptomatte_hash = BKE_cryptomatte_hash(name, name_length); + r_col[0] = BKE_cryptomatte_hash_to_float(cryptomatte_hash); + return true; +} + static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_layer, const char *prefix, const float fpos[2], @@ -214,6 +252,7 @@ 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], @@ -297,7 +336,7 @@ static bool eyedropper_cryptomatte_sample_fl(bContext *C, ED_region_tag_redraw(CTX_wm_region(C)); } - if (!area || !ELEM(area->spacetype, SPACE_IMAGE, SPACE_NODE, SPACE_CLIP)) { + if (!area || !ELEM(area->spacetype, SPACE_IMAGE, SPACE_NODE, SPACE_CLIP, SPACE_VIEW3D)) { return false; } @@ -334,7 +373,9 @@ static bool eyedropper_cryptomatte_sample_fl(bContext *C, } } - if (fpos[0] < 0.0f || fpos[1] < 0.0f || fpos[0] >= 1.0f || fpos[1] >= 1.0f) { + if (area->spacetype != SPACE_VIEW3D && + (fpos[0] < 0.0f || fpos[1] < 0.0f || fpos[0] >= 1.0f || fpos[1] >= 1.0f)) + { return false; } @@ -352,6 +393,23 @@ static bool eyedropper_cryptomatte_sample_fl(bContext *C, ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1); prefix[MAX_NAME] = '\0'; + if (area->spacetype == SPACE_VIEW3D) { + wmWindow *win_prev = CTX_wm_window(C); + ScrArea *area_prev = CTX_wm_area(C); + ARegion *region_prev = CTX_wm_region(C); + + CTX_wm_window_set(C, win); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + const bool success = eyedropper_cryptomatte_sample_view3d_fl(C, prefix, mval, r_col); + + CTX_wm_window_set(C, win_prev); + CTX_wm_area_set(C, area_prev); + CTX_wm_region_set(C, region_prev); + + return success; + } if (node->custom1 == CMP_NODE_CRYPTOMATTE_SOURCE_RENDER) { return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col); } diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 03c16dcba7a..31a12ff1aba 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -54,7 +54,7 @@ * \{ */ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render( - const bNode &node, const bool use_meta_data) + const bNode &node, const bool build_meta_data) { blender::bke::cryptomatte::CryptomatteSessionPtr session; @@ -64,22 +64,8 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no } BLI_assert(GS(scene->id.name) == ID_SCE); - if (use_meta_data) { - Render *render = RE_GetSceneRender(scene); - 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)); - } + session = blender::bke::cryptomatte::CryptomatteSessionPtr( + BKE_cryptomatte_init_from_scene(scene, build_meta_data)); return session; } @@ -107,7 +93,7 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no } static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node( - const Scene &scene, const bNode &node, const bool use_meta_data) + const Scene &scene, const bNode &node, const bool build_meta_data) { blender::bke::cryptomatte::CryptomatteSessionPtr session; if (node.type != CMP_NODE_CRYPTOMATTE) { @@ -116,7 +102,7 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no switch (node.custom1) { case CMP_NODE_CRYPTOMATTE_SOURCE_RENDER: { - return cryptomatte_init_from_node_render(node, use_meta_data); + return cryptomatte_init_from_node_render(node, build_meta_data); } case CMP_NODE_CRYPTOMATTE_SOURCE_IMAGE: {