tornavis/source/blender/compositor/nodes/COM_CryptomatteNode.cc

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_NODE_CRYPTOMATTE_SOURCE_RENDER:
input_operations_from_render_source(context, node, input_operations);
break;
case CMP_NODE_CRYPTOMATTE_SOURCE_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