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

279 lines
9.9 KiB
C++

/*
* 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 2018, Blender Foundation.
*/
#include "COM_CryptomatteNode.h"
#include "BKE_node.h"
#include "BLI_assert.h"
#include "BLI_hash_mm3.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "COM_ConvertOperation.h"
#include "COM_CryptomatteOperation.h"
#include "COM_MultilayerImageOperation.h"
#include "COM_RenderLayersProg.h"
#include "COM_SetAlphaMultiplyOperation.h"
#include "COM_SetColorOperation.h"
#include <iterator>
#include <string>
namespace blender::compositor {
/** \name Cryptomatte base
* \{ */
void CryptomatteBaseNode::convertToOperations(NodeConverter &converter,
const CompositorContext &context) const
{
NodeOutput *output_image_socket = this->getOutputSocket(0);
bNode *node = this->getbNode();
NodeCryptomatte *cryptomatte_settings = static_cast<NodeCryptomatte *>(node->storage);
CryptomatteOperation *cryptomatte_operation = create_cryptomatte_operation(
converter, context, *node, cryptomatte_settings);
converter.addOperation(cryptomatte_operation);
NodeOutput *output_matte_socket = this->getOutputSocket(1);
SeparateChannelOperation *extract_mask_operation = new SeparateChannelOperation;
extract_mask_operation->setChannel(3);
converter.addOperation(extract_mask_operation);
converter.addLink(cryptomatte_operation->getOutputSocket(0),
extract_mask_operation->getInputSocket(0));
converter.mapOutputSocket(output_matte_socket, extract_mask_operation->getOutputSocket(0));
NodeInput *input_image_socket = this->getInputSocket(0);
SetAlphaMultiplyOperation *apply_mask_operation = new SetAlphaMultiplyOperation();
converter.mapInputSocket(input_image_socket, apply_mask_operation->getInputSocket(0));
converter.addOperation(apply_mask_operation);
converter.addLink(extract_mask_operation->getOutputSocket(0),
apply_mask_operation->getInputSocket(1));
converter.mapOutputSocket(output_image_socket, apply_mask_operation->getOutputSocket(0));
NodeOutput *output_pick_socket = this->getOutputSocket(2);
SetAlphaMultiplyOperation *extract_pick_operation = new SetAlphaMultiplyOperation();
converter.addOperation(extract_pick_operation);
converter.addInputValue(extract_pick_operation->getInputSocket(1), 1.0f);
converter.addLink(cryptomatte_operation->getOutputSocket(0),
extract_pick_operation->getInputSocket(0));
converter.mapOutputSocket(output_pick_socket, extract_pick_operation->getOutputSocket(0));
}
/* \} */
/** \name Cryptomatte V2
* \{ */
static std::string prefix_from_node(const CompositorContext &context, const bNode &node)
{
char prefix[MAX_NAME];
ntreeCompositCryptomatteLayerPrefix(context.getScene(), &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) {
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.getViewName())) {
continue;
}
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
if (blender::StringRef(combined_name).startswith(prefix)) {
RenderLayersProg *op = new RenderLayersProg(
render_pass->name, DataType::Color, render_pass->channels);
op->setScene(scene);
op->setLayerId(view_layer_id);
op->setRenderData(context.getRenderData());
op->setViewName(context.getViewName());
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.getFramenumber());
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.getViewName(), 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 (blender::StringRef(combined_name).startswith(prefix)) {
MultilayerColorOperation *op = new MultilayerColorOperation(
render_layer, render_pass, view);
op->setImage(image);
op->setImageUser(iuser);
iuser->layer = layer_index;
op->setFramenumber(context.getFramenumber());
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->setChannel1(0.0f);
op->setChannel2(1.0f);
op->setChannel3(0.0f);
op->setChannel4(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->addObjectIndex(cryptomatte_entry->encoded_hash);
}
for (int i = 0; i < input_operations.size(); ++i) {
converter.addOperation(input_operations[i]);
converter.addLink(input_operations[i]->getOutputSocket(), operation->getInputSocket(i));
}
return operation;
}
/* \} */
/** \name Cryptomatte legacy
* \{ */
CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation(
NodeConverter &converter,
const CompositorContext &UNUSED(context),
const bNode &UNUSED(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->addObjectIndex(cryptomatte_entry->encoded_hash);
}
}
for (int i = 0; i < num_inputs; i++) {
converter.mapInputSocket(this->getInputSocket(i + 1), operation->getInputSocket(i));
}
return operation;
}
/* \} */
} // namespace blender::compositor