It was only used by OpenEXR and Iris images, and saving the Z Buffer in those formats was disabled by default. This option comes from the times prior to the addition of the Multilayer EXR. It also worth noting that it was not possible to save Iris with Depth pass from Blender as internally it is called IRIZ format and it was not exposed. But even after exposing this format option something still was missing as saving and loading ITIZ did not show up the Depth pass. The reason of removal is to make it a more clear match of the ImBuf with a render pass, and use it instead of a custom type in the render result and render pass API. This will simplify the API and also avoid stealing buffers and making shallow copies when showing the render result. For the cases when Depth is needed a Multilayer EXR is to be used, as most likely more than just the Depth will be needed. On a user level this change: - Removes the "Z Buffer" option from the interface. - It preserves existing sockets in compositor nodes, but it will output black image. Also changing the image data-block will remove the socket unless a Multilayer EXR with Depth pass image is selected. - Removes "Depth" socket of the Viewer and Composite nodes. Ref #108618 Pull Request: https://projects.blender.org/blender/blender/pulls/109687
270 lines
11 KiB
C++
270 lines
11 KiB
C++
/* SPDX-FileCopyrightText: 2011 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "COM_ImageNode.h"
|
|
#include "COM_ConvertOperation.h"
|
|
#include "COM_MultilayerImageOperation.h"
|
|
|
|
#include "COM_SetColorOperation.h"
|
|
#include "COM_SetValueOperation.h"
|
|
#include "COM_SetVectorOperation.h"
|
|
|
|
namespace blender::compositor {
|
|
|
|
ImageNode::ImageNode(bNode *editor_node) : Node(editor_node)
|
|
{
|
|
/* pass */
|
|
}
|
|
NodeOperation *ImageNode::do_multilayer_check(NodeConverter &converter,
|
|
RenderLayer *render_layer,
|
|
RenderPass *render_pass,
|
|
Image *image,
|
|
ImageUser *user,
|
|
int framenumber,
|
|
int outputsocket_index,
|
|
int view,
|
|
DataType datatype) const
|
|
{
|
|
NodeOutput *output_socket = this->get_output_socket(outputsocket_index);
|
|
MultilayerBaseOperation *operation = nullptr;
|
|
switch (datatype) {
|
|
case DataType::Value:
|
|
operation = new MultilayerValueOperation(render_layer, render_pass, view);
|
|
break;
|
|
case DataType::Vector:
|
|
operation = new MultilayerVectorOperation(render_layer, render_pass, view);
|
|
break;
|
|
case DataType::Color:
|
|
operation = new MultilayerColorOperation(render_layer, render_pass, view);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
operation->set_image(image);
|
|
operation->set_image_user(user);
|
|
operation->set_framenumber(framenumber);
|
|
|
|
converter.add_operation(operation);
|
|
converter.map_output_socket(output_socket, operation->get_output_socket());
|
|
|
|
return operation;
|
|
}
|
|
|
|
void ImageNode::convert_to_operations(NodeConverter &converter,
|
|
const CompositorContext &context) const
|
|
{
|
|
/** Image output */
|
|
NodeOutput *output_image = this->get_output_socket(0);
|
|
const bNode *editor_node = this->get_bnode();
|
|
Image *image = (Image *)editor_node->id;
|
|
ImageUser *imageuser = (ImageUser *)editor_node->storage;
|
|
int framenumber = context.get_framenumber();
|
|
bool output_straight_alpha = (editor_node->custom1 & CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT) != 0;
|
|
BKE_image_user_frame_calc(image, imageuser, context.get_framenumber());
|
|
/* Force a load, we assume #ImageUser index will be set OK anyway. */
|
|
if (image && image->type == IMA_TYPE_MULTILAYER) {
|
|
bool is_multilayer_ok = false;
|
|
ImBuf *ibuf = BKE_image_acquire_ibuf(image, imageuser, nullptr);
|
|
if (image->rr) {
|
|
RenderLayer *rl = (RenderLayer *)BLI_findlink(&image->rr->layers, imageuser->layer);
|
|
if (rl) {
|
|
is_multilayer_ok = true;
|
|
|
|
for (int64_t index = 0; index < outputs_.size(); index++) {
|
|
NodeOutput *socket = outputs_[index];
|
|
NodeOperation *operation = nullptr;
|
|
bNodeSocket *bnode_socket = socket->get_bnode_socket();
|
|
NodeImageLayer *storage = (NodeImageLayer *)bnode_socket->storage;
|
|
RenderPass *rpass = (RenderPass *)BLI_findstring(
|
|
&rl->passes, storage->pass_name, offsetof(RenderPass, name));
|
|
int view = 0;
|
|
|
|
if (STREQ(storage->pass_name, RE_PASSNAME_COMBINED) &&
|
|
STREQ(bnode_socket->name, "Alpha")) {
|
|
/* Alpha output is already handled with the associated combined output. */
|
|
continue;
|
|
}
|
|
|
|
/* returns the image view to use for the current active view */
|
|
if (BLI_listbase_count_at_most(&image->rr->views, 2) > 1) {
|
|
const int view_image = imageuser->view;
|
|
const bool is_allview = (view_image == 0); /* if view selected == All (0) */
|
|
|
|
if (is_allview) {
|
|
/* 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 = view_image - 1;
|
|
}
|
|
}
|
|
|
|
if (rpass) {
|
|
switch (rpass->channels) {
|
|
case 1:
|
|
operation = do_multilayer_check(converter,
|
|
rl,
|
|
rpass,
|
|
image,
|
|
imageuser,
|
|
framenumber,
|
|
index,
|
|
view,
|
|
DataType::Value);
|
|
break;
|
|
/* using image operations for both 3 and 4 channels (RGB and RGBA respectively) */
|
|
/* XXX any way to detect actual vector images? */
|
|
case 3:
|
|
operation = do_multilayer_check(converter,
|
|
rl,
|
|
rpass,
|
|
image,
|
|
imageuser,
|
|
framenumber,
|
|
index,
|
|
view,
|
|
DataType::Vector);
|
|
break;
|
|
case 4:
|
|
operation = do_multilayer_check(converter,
|
|
rl,
|
|
rpass,
|
|
image,
|
|
imageuser,
|
|
framenumber,
|
|
index,
|
|
view,
|
|
DataType::Color);
|
|
break;
|
|
default:
|
|
/* dummy operation is added below */
|
|
break;
|
|
}
|
|
if (index == 0 && operation) {
|
|
converter.add_preview(operation->get_output_socket());
|
|
}
|
|
if (STREQ(rpass->name, RE_PASSNAME_COMBINED) && !(bnode_socket->flag & SOCK_UNAVAIL)) {
|
|
for (NodeOutput *alpha_socket : get_output_sockets()) {
|
|
bNodeSocket *bnode_alpha_socket = alpha_socket->get_bnode_socket();
|
|
if (!STREQ(bnode_alpha_socket->name, "Alpha")) {
|
|
continue;
|
|
}
|
|
NodeImageLayer *alpha_storage = (NodeImageLayer *)bnode_socket->storage;
|
|
if (!STREQ(alpha_storage->pass_name, RE_PASSNAME_COMBINED)) {
|
|
continue;
|
|
}
|
|
SeparateChannelOperation *separate_operation;
|
|
separate_operation = new SeparateChannelOperation();
|
|
separate_operation->set_channel(3);
|
|
converter.add_operation(separate_operation);
|
|
converter.add_link(operation->get_output_socket(),
|
|
separate_operation->get_input_socket(0));
|
|
converter.map_output_socket(alpha_socket, separate_operation->get_output_socket());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* In case we can't load the layer. */
|
|
if (operation == nullptr) {
|
|
converter.set_invalid_output(get_output_socket(index));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
BKE_image_release_ibuf(image, ibuf, nullptr);
|
|
|
|
/* without this, multilayer that fail to load will crash blender #32490. */
|
|
if (is_multilayer_ok == false) {
|
|
for (NodeOutput *output : get_output_sockets()) {
|
|
converter.set_invalid_output(output);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
const int64_t number_of_outputs = get_output_sockets().size();
|
|
if (number_of_outputs > 0) {
|
|
ImageOperation *operation = new ImageOperation();
|
|
operation->set_image(image);
|
|
operation->set_image_user(imageuser);
|
|
operation->set_framenumber(framenumber);
|
|
operation->set_render_data(context.get_render_data());
|
|
operation->set_view_name(context.get_view_name());
|
|
converter.add_operation(operation);
|
|
|
|
if (output_straight_alpha) {
|
|
NodeOperation *alpha_convert_operation = new ConvertPremulToStraightOperation();
|
|
|
|
converter.add_operation(alpha_convert_operation);
|
|
converter.map_output_socket(output_image, alpha_convert_operation->get_output_socket());
|
|
converter.add_link(operation->get_output_socket(0),
|
|
alpha_convert_operation->get_input_socket(0));
|
|
}
|
|
else {
|
|
converter.map_output_socket(output_image, operation->get_output_socket());
|
|
}
|
|
|
|
converter.add_preview(operation->get_output_socket());
|
|
}
|
|
|
|
if (number_of_outputs > 1) {
|
|
NodeOutput *alpha_image = this->get_output_socket(1);
|
|
ImageAlphaOperation *alpha_operation = new ImageAlphaOperation();
|
|
alpha_operation->set_image(image);
|
|
alpha_operation->set_image_user(imageuser);
|
|
alpha_operation->set_framenumber(framenumber);
|
|
alpha_operation->set_render_data(context.get_render_data());
|
|
alpha_operation->set_view_name(context.get_view_name());
|
|
converter.add_operation(alpha_operation);
|
|
|
|
converter.map_output_socket(alpha_image, alpha_operation->get_output_socket());
|
|
}
|
|
else {
|
|
/* happens when unlinking image datablock from multilayer node */
|
|
for (int i = 2; i < number_of_outputs; i++) {
|
|
NodeOutput *output = this->get_output_socket(i);
|
|
NodeOperation *operation = nullptr;
|
|
switch (output->get_data_type()) {
|
|
case DataType::Value: {
|
|
SetValueOperation *valueoperation = new SetValueOperation();
|
|
valueoperation->set_value(0.0f);
|
|
operation = valueoperation;
|
|
break;
|
|
}
|
|
case DataType::Vector: {
|
|
SetVectorOperation *vectoroperation = new SetVectorOperation();
|
|
vectoroperation->setX(0.0f);
|
|
vectoroperation->setY(0.0f);
|
|
vectoroperation->setW(0.0f);
|
|
operation = vectoroperation;
|
|
break;
|
|
}
|
|
case DataType::Color: {
|
|
SetColorOperation *coloroperation = new SetColorOperation();
|
|
coloroperation->set_channel1(0.0f);
|
|
coloroperation->set_channel2(0.0f);
|
|
coloroperation->set_channel3(0.0f);
|
|
coloroperation->set_channel4(0.0f);
|
|
operation = coloroperation;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (operation) {
|
|
/* not supporting multiview for this generic case */
|
|
converter.add_operation(operation);
|
|
converter.map_output_socket(output, operation->get_output_socket());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // namespace blender::compositor
|
|
|
|
} // namespace blender::compositor
|