Nodes: add ability to preview shader output nodes(AOV, group, material) #110945

Merged
Brecht Van Lommel merged 2 commits from Kdaf/blender:GSOC-preview-output-nodes into main 2023-08-11 16:48:03 +02:00
3 changed files with 90 additions and 65 deletions
Showing only changes of commit 63218b9f7c - Show all commits

View File

@ -305,6 +305,7 @@ void NODE_OT_group_edit(wmOperatorType *ot);
void update_multi_input_indices_for_removed_links(bNode &node);
bool all_links_muted(const bNodeSocket &socket);
bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out);
void NODE_OT_link(wmOperatorType *ot);
void NODE_OT_link_make(wmOperatorType *ot);

View File

@ -67,8 +67,6 @@ struct NodeInsertOfsData {
namespace blender::ed::space_node {
bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out);
static void clear_picking_highlight(ListBase *links)
{
LISTBASE_FOREACH (bNodeLink *, link, links) {

View File

@ -26,6 +26,9 @@
#include "DNA_material_types.h"
#include "DNA_world_types.h"
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "BKE_colortools.h"
#include "BKE_compute_contexts.hh"
#include "BKE_context.h"
@ -74,8 +77,8 @@ struct ShaderNodesPreviewJob {
/* TreePath used to locate the nodetree.
* bNodeTreePath elements have some listbase pointers which should not be used. */
Vector<bNodeTreePath *> treepath_copy;
Vector<bNode *> AOV_nodes;
Vector<bNode *> shader_nodes;
Vector<NodeSocketPair> AOV_nodes;
Vector<NodeSocketPair> shader_nodes;
bNode *rendering_node;
bool rendering_AOVs;
@ -240,44 +243,34 @@ static Scene *preview_prepare_scene(const Main *bmain,
/** \name Preview rendering
* \{ */
static void node_find_preview_socket(const bNode *&node, const bNodeSocket *&socket)
/**
* Follows some rules to determine the previewed socket and node associated.
* We first seek for an output socket of the node, if none if found, the node is an output node, and thus seek for an input socket.
*/
static bNodeSocket *node_find_preview_socket(bNodeTree &ntree, bNode &node)
{
LISTBASE_FOREACH (bNodeSocket *, socket_iter, &node->outputs) {
if (socket_iter->is_visible()) {
socket = socket_iter;
break;
}
}
bNodeSocket *socket = get_main_socket(ntree, node, SOCK_OUT);
if (socket == nullptr) {
bNodeSocket *input_socket = nullptr;
LISTBASE_FOREACH (bNodeSocket *, socket_iter, &node->inputs) {
if (socket_iter->is_visible() && socket_iter->link != nullptr) {
input_socket = socket_iter;
break;
socket = get_main_socket(ntree, node, SOCK_IN);
if (socket != nullptr && socket->link == nullptr) {
if (!(ELEM(socket->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA))) {
/* We can not preview a socket with no link and no manual value. */
return nullptr;
}
}
if (input_socket == nullptr) {
socket = nullptr;
return;
}
node = input_socket->link->fromnode;
node_find_preview_socket(node, socket);
}
return socket;
}
static void node_find_preview_socket(bNode *&node, bNodeSocket *&socket) {
const bNode *const_node = node;
const bNodeSocket *const_socket = socket;
node_find_preview_socket(const_node, const_socket);
node = const_cast<bNode *>(const_node);
socket = const_cast<bNodeSocket *>(const_socket);
static bool socket_use_aov(const bNodeSocket *socket) {
return socket == nullptr || socket->type != SOCK_SHADER;
}
static bool node_use_aov(const bNode *node)
static bool node_use_aov(bNodeTree &ntree, const bNode *node)
{
const bNodeSocket *socket_preview = nullptr;
node_find_preview_socket(node, socket_preview);
return socket_preview == nullptr || socket_preview->type != SOCK_SHADER;
bNode *node_preview = const_cast<bNode *>(node);
bNodeSocket *socket_preview = node_find_preview_socket(ntree, *node_preview);
return socket_use_aov(socket_preview);
}
static ImBuf *get_image_from_viewlayer_and_pass(RenderResult &rr,
@ -326,7 +319,7 @@ ImBuf *node_preview_acquire_ibuf(bNodeTree &ntree,
else {
/* When the render process is started, the user must see that the preview area is open. */
ImBuf *image_latest = nullptr;
if (node_use_aov(&node)) {
if (node_use_aov(ntree, &node)) {
image_latest = get_image_from_viewlayer_and_pass(*rr, nullptr, node.name);
}
else {
@ -411,17 +404,21 @@ static void connect_nested_node_to_node(const Span<bNodeTreePath *> treepath,
/* Connect the node to the output of the first nodetree from `treepath`. Last element of `treepath`
* should be the path to the node's nodetree */
static void connect_node_to_surface_output(const Span<bNodeTreePath *> treepath,
bNode &node,
NodeSocketPair nodesocket,
bNode &output_node)
{
bNodeSocket *out_surface_socket = nullptr;
bNodeTree *main_nt = treepath.first()->nodetree;
bNodeSocket *socket_preview = nullptr;
bNode *node_preview = &node;
node_find_preview_socket(node_preview, socket_preview);
bNode *node_preview = nodesocket.first;
bNodeSocket *socket_preview = nodesocket.second;
if (socket_preview == nullptr) {
return;
}
if (socket_preview->in_out == SOCK_IN) {
BLI_assert(socket_preview->link != nullptr);
node_preview = socket_preview->link->fromnode;
socket_preview = socket_preview->link->fromsock;
}
/* Ensure output is usable. */
out_surface_socket = nodeFindSocket(&output_node, SOCK_IN, "Surface");
if (out_surface_socket->link) {
@ -430,32 +427,60 @@ static void connect_node_to_surface_output(const Span<bNodeTreePath *> treepath,
}
connect_nested_node_to_node(
treepath, *node_preview, *socket_preview, output_node, *out_surface_socket, node.name);
treepath, *node_preview, *socket_preview, output_node, *out_surface_socket, nodesocket.first->name);
BKE_ntree_update_main_tree(G.pr_main, main_nt, nullptr);
}
/* Connect the nodes to some aov nodes located in the first nodetree from `treepath`. Last element
* of `treepath` should be the path to the nodes nodetree. */
static void connect_nodes_to_aovs(const Span<bNodeTreePath *> treepath, const Span<bNode *> &nodes)
static void connect_nodes_to_aovs(const Span<bNodeTreePath *> treepath, const Span<NodeSocketPair> &nodesocket_span)
{
if (nodes.size() == 0) {
if (nodesocket_span.size() == 0) {
return;
}
bNodeTree *main_nt = treepath.first()->nodetree;
for (bNode *node : nodes) {
bNodeSocket *socket_preview = nullptr;
bNode *node_preview = node;
node_find_preview_socket(node_preview, socket_preview);
bNodeTree *active_nt = treepath.last()->nodetree;
for (NodeSocketPair nodesocket : nodesocket_span) {
bNode *node_preview = nodesocket.first;
bNodeSocket *socket_preview = nodesocket.second;
bNode *aov_node = nodeAddStaticNode(nullptr, main_nt, SH_NODE_OUTPUT_AOV);
strcpy(reinterpret_cast<NodeShaderOutputAOV *>(aov_node->storage)->name, node->name);
strcpy(reinterpret_cast<NodeShaderOutputAOV *>(aov_node->storage)->name, nodesocket.first->name);
if (socket_preview == nullptr) {
continue;
}
bNodeSocket *aov_socket = nodeFindSocket(aov_node, SOCK_IN, "Color");
if (socket_preview->in_out == SOCK_IN) {
if (socket_preview->link == nullptr) {
/**
* Copy the custom value of the socket directly to the AOV node.
* If the socket does not support custom values, it will justl render black.
*/
float vec[4] = {0., 0., 0., 1.};
PointerRNA ptr;
switch (socket_preview->type) {
case SOCK_FLOAT:
RNA_pointer_create((ID *)active_nt, &RNA_NodeSocket, socket_preview, &ptr);
vec[0] = RNA_float_get(&ptr, "default_value");
vec[1] = vec[0];
vec[2] = vec[0];
break;
case SOCK_VECTOR:
case SOCK_RGBA:
RNA_pointer_create((ID *)active_nt, &RNA_NodeSocket, socket_preview, &ptr);
RNA_float_get_array(&ptr, "default_value", vec);
break;
}
RNA_pointer_create((ID *)active_nt, &RNA_NodeSocket, aov_socket, &ptr);
RNA_float_set_array(&ptr, "default_value", vec);
continue;
} else {
node_preview = socket_preview->link->fromnode;
socket_preview = socket_preview->link->fromsock;
}
}
connect_nested_node_to_node(
treepath, *node_preview, *socket_preview, *aov_node, *aov_socket, node->name);
treepath, *node_preview, *socket_preview, *aov_node, *aov_socket, nodesocket.first->name);
}
BKE_ntree_update_main_tree(G.pr_main, main_nt, nullptr);
}
@ -470,17 +495,17 @@ static bool nodetree_previews_break(void *spv)
static bool prepare_viewlayer_update(void *pvl_data, ViewLayer *vl, Depsgraph *depsgraph)
{
bNode *node = nullptr;
NodeSocketPair nodesocket = {nullptr, nullptr};
ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(pvl_data);
for (bNode *node_iter : job_data->shader_nodes) {
if (STREQ(vl->name, node_iter->name)) {
node = node_iter;
job_data->rendering_node = node_iter;
for (NodeSocketPair nodesocket_iter : job_data->shader_nodes) {
if (STREQ(vl->name, nodesocket_iter.first->name)) {
nodesocket = nodesocket_iter;
job_data->rendering_node = nodesocket_iter.first;
job_data->rendering_AOVs = false;
break;
}
}
if (node == nullptr) {
if (nodesocket.first == nullptr) {
job_data->rendering_node = nullptr;
job_data->rendering_AOVs = true;
/* The AOV layer is the default `ViewLayer` of the scene(which should be the first one). */
@ -496,7 +521,7 @@ static bool prepare_viewlayer_update(void *pvl_data, ViewLayer *vl, Depsgraph *d
job_data->mat_output_copy,
displacement_socket);
}
connect_node_to_surface_output(job_data->treepath_copy, *node, *job_data->mat_output_copy);
connect_node_to_surface_output(job_data->treepath_copy, nodesocket, *job_data->mat_output_copy);
if (depsgraph != nullptr) {
/* Used to refresh the dependency graph so that the material can be updated. */
@ -529,10 +554,10 @@ static void all_nodes_preview_update(void *npv, RenderResult *rr, rcti * /*rect*
}
}
if (job_data->rendering_AOVs) {
for (bNode *node : job_data->AOV_nodes) {
ImBuf *&image_cached = job_data->tree_previews->previews_map.lookup_or_add(node->identifier,
for (NodeSocketPair nodesocket_iter : job_data->AOV_nodes) {
ImBuf *&image_cached = job_data->tree_previews->previews_map.lookup_or_add(nodesocket_iter.first->identifier,
nullptr);
ImBuf *image_latest = get_image_from_viewlayer_and_pass(*rr, nullptr, node->name);
ImBuf *image_latest = get_image_from_viewlayer_and_pass(*rr, nullptr, nodesocket_iter.first->name);
if (image_latest == nullptr) {
continue;
}
@ -562,13 +587,13 @@ static void preview_render(ShaderNodesPreviewJob &job_data)
/* Create the AOV passes for the viewlayer. */
ViewLayer *AOV_layer = static_cast<ViewLayer *>(scene->view_layers.first);
for (bNode *node : job_data.shader_nodes) {
ViewLayer *vl = BKE_view_layer_add(scene, node->name, AOV_layer, VIEWLAYER_ADD_COPY);
strcpy(vl->name, node->name);
for (NodeSocketPair nodesocket_iter : job_data.shader_nodes) {
ViewLayer *vl = BKE_view_layer_add(scene, nodesocket_iter.first->name, AOV_layer, VIEWLAYER_ADD_COPY);
strcpy(vl->name, nodesocket_iter.first->name);
}
for (bNode *node : job_data.AOV_nodes) {
for (NodeSocketPair nodesocket_iter : job_data.AOV_nodes) {
ViewLayerAOV *aov = BKE_view_layer_add_aov(AOV_layer);
strcpy(aov->name, node->name);
strcpy(aov->name, nodesocket_iter.first->name);
}
scene->r.xsch = job_data.tree_previews->preview_size;
scene->r.ysch = job_data.tree_previews->preview_size;
@ -681,11 +706,12 @@ static void shader_preview_startjob(void *customdata,
}
continue;
}
if (node_use_aov(node)) {
job_data->AOV_nodes.append(node);
bNodeSocket *preview_socket = node_find_preview_socket(*active_nodetree, *node);
if (socket_use_aov(preview_socket)) {
job_data->AOV_nodes.append({node, preview_socket});
}
else {
job_data->shader_nodes.append(node);
job_data->shader_nodes.append({node, preview_socket});
}
}