Files
test2/source/blender/compositor/nodes/COM_CryptomatteNode.cc
Omar Emara 45dcc61fd2 Fix #115859: Cryptomatte node considers preview channels
The Cryptomatte node considers preview channels as part of the
Cryptomatte layers, producing bad pick outputs at best and bad mattes at
worst.

The Cryptomatte specification specifies that a layer with the same name
as the typename is a (Now deprecated) preview layer, so it should be
ignored, which is what the patch does.

Pull Request: https://projects.blender.org/blender/blender/pulls/115879
2023-12-07 10:00:55 +01:00

273 lines
9.6 KiB
C++

/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_string.h"
#include "BKE_node.hh"
#include "NOD_composite.hh"
#include "COM_ConvertOperation.h"
#include "COM_CryptomatteNode.h"
#include "COM_MultilayerImageOperation.h"
#include "COM_RenderLayersProg.h"
#include "COM_SetAlphaMultiplyOperation.h"
#include "COM_SetAlphaReplaceOperation.h"
#include "COM_SetColorOperation.h"
namespace blender::compositor {
/* -------------------------------------------------------------------- */
/** \name Cryptomatte Base
* \{ */
void CryptomatteBaseNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
NodeOutput *output_image_socket = this->get_output_socket(0);
const bNode *node = this->get_bnode();
const NodeCryptomatte *cryptomatte_settings = static_cast<const NodeCryptomatte *>(
node->storage);
CryptomatteOperation *cryptomatte_operation = create_cryptomatte_operation(
converter, context, *node, cryptomatte_settings);
converter.add_operation(cryptomatte_operation);
NodeOutput *output_matte_socket = this->get_output_socket(1);
SeparateChannelOperation *extract_mask_operation = new SeparateChannelOperation;
extract_mask_operation->set_channel(3);
converter.add_operation(extract_mask_operation);
converter.add_link(cryptomatte_operation->get_output_socket(0),
extract_mask_operation->get_input_socket(0));
converter.map_output_socket(output_matte_socket, extract_mask_operation->get_output_socket(0));
NodeInput *input_image_socket = this->get_input_socket(0);
SetAlphaMultiplyOperation *apply_mask_operation = new SetAlphaMultiplyOperation();
converter.map_input_socket(input_image_socket, apply_mask_operation->get_input_socket(0));
converter.add_operation(apply_mask_operation);
converter.add_link(extract_mask_operation->get_output_socket(0),
apply_mask_operation->get_input_socket(1));
converter.map_output_socket(output_image_socket, apply_mask_operation->get_output_socket(0));
NodeOutput *output_pick_socket = this->get_output_socket(2);
SetAlphaReplaceOperation *extract_pick_operation = new SetAlphaReplaceOperation();
converter.add_operation(extract_pick_operation);
converter.add_input_value(extract_pick_operation->get_input_socket(1), 1.0f);
converter.add_link(cryptomatte_operation->get_output_socket(0),
extract_pick_operation->get_input_socket(0));
converter.map_output_socket(output_pick_socket, extract_pick_operation->get_output_socket(0));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Cryptomatte V2
* \{ */
static std::string prefix_from_node(const CompositorContext &context, const bNode &node)
{
char prefix[MAX_NAME];
ntreeCompositCryptomatteLayerPrefix(context.get_scene(), &node, prefix, sizeof(prefix));
return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix)));
}
static std::string combined_layer_pass_name(RenderLayer *render_layer, RenderPass *render_pass)
{
if (render_layer->name[0] == '\0') {
return std::string(render_pass->name,
BLI_strnlen(render_pass->name, sizeof(render_pass->name)));
}
std::string combined_name =
blender::StringRef(render_layer->name,
BLI_strnlen(render_layer->name, sizeof(render_layer->name))) +
"." +
blender::StringRef(render_pass->name,
BLI_strnlen(render_pass->name, sizeof(render_pass->name)));
return combined_name;
}
void CryptomatteNode::input_operations_from_render_source(
const CompositorContext &context,
const bNode &node,
Vector<NodeOperation *> &r_input_operations)
{
Scene *scene = (Scene *)node.id;
if (!scene) {
return;
}
BLI_assert(GS(scene->id.name) == ID_SCE);
Render *render = RE_GetSceneRender(scene);
RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr;
if (!render_result) {
if (render) {
RE_ReleaseResult(render);
}
return;
}
short view_layer_id = 0;
const std::string prefix = prefix_from_node(context, node);
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) {
if (context.has_explicit_view() && !STREQ(render_pass->view, context.get_view_name())) {
continue;
}
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
if (combined_name != prefix && blender::StringRef(combined_name).startswith(prefix)) {
RenderLayersProg *op = new RenderLayersProg(
render_pass->name, DataType::Color, render_pass->channels);
op->set_scene(scene);
op->set_layer_id(view_layer_id);
op->set_render_data(context.get_render_data());
op->set_view_name(context.get_view_name());
r_input_operations.append(op);
}
}
}
}
RE_ReleaseResult(render);
}
void CryptomatteNode::input_operations_from_image_source(
const CompositorContext &context,
const bNode &node,
Vector<NodeOperation *> &r_input_operations)
{
NodeCryptomatte *cryptomatte_settings = (NodeCryptomatte *)node.storage;
Image *image = (Image *)node.id;
if (!image) {
return;
}
BLI_assert(GS(image->id.name) == ID_IM);
if (image->type != IMA_TYPE_MULTILAYER) {
return;
}
ImageUser *iuser = &cryptomatte_settings->iuser;
BKE_image_user_frame_calc(image, iuser, context.get_framenumber());
ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr);
if (image->rr) {
int view = 0;
if (BLI_listbase_count_at_most(&image->rr->views, 2) > 1) {
if (iuser->view == 0) {
/* Heuristic to match image name with scene names, check if the view name exists in the
* image. */
view = BLI_findstringindex(
&image->rr->views, context.get_view_name(), offsetof(RenderView, name));
if (view == -1) {
view = 0;
}
}
else {
view = iuser->view - 1;
}
}
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(
render_layer->name, BLI_strnlen(render_layer->name, sizeof(render_layer->name)))))
{
continue;
}
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
if (combined_name != prefix && blender::StringRef(combined_name).startswith(prefix)) {
MultilayerColorOperation *op = new MultilayerColorOperation(
render_layer, render_pass, view);
op->set_image(image);
op->set_image_user(iuser);
iuser->layer = layer_index;
op->set_framenumber(context.get_framenumber());
r_input_operations.append(op);
}
}
break;
}
}
BKE_image_release_ibuf(image, ibuf, nullptr);
}
Vector<NodeOperation *> CryptomatteNode::create_input_operations(const CompositorContext &context,
const bNode &node)
{
Vector<NodeOperation *> input_operations;
switch (node.custom1) {
case CMP_CRYPTOMATTE_SRC_RENDER:
input_operations_from_render_source(context, node, input_operations);
break;
case CMP_CRYPTOMATTE_SRC_IMAGE:
input_operations_from_image_source(context, node, input_operations);
break;
}
if (input_operations.is_empty()) {
SetColorOperation *op = new SetColorOperation();
op->set_channel1(0.0f);
op->set_channel2(1.0f);
op->set_channel3(0.0f);
op->set_channel4(0.0f);
input_operations.append(op);
}
return input_operations;
}
CryptomatteOperation *CryptomatteNode::create_cryptomatte_operation(
NodeConverter &converter,
const CompositorContext &context,
const bNode &node,
const NodeCryptomatte *cryptomatte_settings) const
{
Vector<NodeOperation *> input_operations = create_input_operations(context, node);
CryptomatteOperation *operation = new CryptomatteOperation(input_operations.size());
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) {
operation->add_object_index(cryptomatte_entry->encoded_hash);
}
for (int i = 0; i < input_operations.size(); ++i) {
converter.add_operation(input_operations[i]);
converter.add_link(input_operations[i]->get_output_socket(), operation->get_input_socket(i));
}
return operation;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Cryptomatte Legacy
* \{ */
CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation(
NodeConverter &converter,
const CompositorContext & /*context*/,
const bNode & /*node*/,
const NodeCryptomatte *cryptomatte_settings) const
{
const int num_inputs = inputs_.size() - 1;
CryptomatteOperation *operation = new CryptomatteOperation(num_inputs);
if (cryptomatte_settings) {
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) {
operation->add_object_index(cryptomatte_entry->encoded_hash);
}
}
for (int i = 0; i < num_inputs; i++) {
converter.map_input_socket(this->get_input_socket(i + 1), operation->get_input_socket(i));
}
return operation;
}
/** \} */
} // namespace blender::compositor