2011-10-23 17:52:20 +00:00
|
|
|
/*
|
2011-09-05 21:01:50 +00:00
|
|
|
* 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
|
2018-06-01 18:19:39 +02:00
|
|
|
* of the License, or (at your option) any later version.
|
2011-09-05 21:01:50 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2007 Blender Foundation.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
* \ingroup nodes
|
2011-09-05 21:01:50 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
2019-02-27 12:34:56 +11:00
|
|
|
#include "DNA_light_types.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "DNA_material_types.h"
|
|
|
|
#include "DNA_node_types.h"
|
2011-11-07 22:14:48 +00:00
|
|
|
#include "DNA_scene_types.h"
|
2013-03-18 16:34:57 +00:00
|
|
|
#include "DNA_space_types.h"
|
2011-11-02 18:55:32 +00:00
|
|
|
#include "DNA_world_types.h"
|
2014-07-11 16:51:04 +09:00
|
|
|
#include "DNA_linestyle_types.h"
|
2017-10-16 17:15:03 -02:00
|
|
|
#include "DNA_workspace_types.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
|
|
|
|
#include "BLI_listbase.h"
|
|
|
|
#include "BLI_threads.h"
|
|
|
|
#include "BLI_utildefines.h"
|
2019-03-05 17:24:31 +01:00
|
|
|
#include "BLI_alloca.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2015-08-16 17:32:01 +10:00
|
|
|
#include "BLT_translation.h"
|
2011-11-09 15:00:11 +00:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
#include "BKE_context.h"
|
2014-07-16 13:53:00 +09:00
|
|
|
#include "BKE_linestyle.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "BKE_node.h"
|
2011-11-07 22:14:48 +00:00
|
|
|
#include "BKE_scene.h"
|
2019-06-26 12:03:15 +02:00
|
|
|
#include "BKE_library.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
#include "RNA_access.h"
|
|
|
|
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "GPU_material.h"
|
|
|
|
|
|
|
|
#include "RE_shader_ext.h"
|
|
|
|
|
2016-06-17 12:14:36 +02:00
|
|
|
#include "NOD_common.h"
|
|
|
|
|
2012-08-06 18:49:28 +00:00
|
|
|
#include "node_common.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "node_exec.h"
|
|
|
|
#include "node_util.h"
|
|
|
|
#include "node_shader_util.h"
|
|
|
|
|
2018-12-03 17:19:04 +01:00
|
|
|
typedef struct nTreeTags {
|
2019-04-17 06:17:24 +02:00
|
|
|
float ssr_id, sss_id;
|
2018-12-03 17:19:04 +01:00
|
|
|
} nTreeTags;
|
|
|
|
|
|
|
|
static void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags);
|
2018-11-17 17:08:21 +01:00
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool shader_tree_poll(const bContext *C, bNodeTreeType *UNUSED(treetype))
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
const char *engine_id = scene->r.engine;
|
2017-10-16 17:15:03 -02:00
|
|
|
|
2019-04-29 20:12:09 +10:00
|
|
|
/* Allow empty engine string too,
|
|
|
|
* this is from older versions that didn't have registerable engines yet. */
|
2019-04-17 06:17:24 +02:00
|
|
|
return (engine_id[0] == '\0' || STREQ(engine_id, RE_engine_id_CYCLES) ||
|
|
|
|
!BKE_scene_use_shading_nodes_custom(scene));
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2011-11-02 18:55:32 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static void shader_get_from_context(const bContext *C,
|
|
|
|
bNodeTreeType *UNUSED(treetype),
|
|
|
|
bNodeTree **r_ntree,
|
|
|
|
ID **r_id,
|
|
|
|
ID **r_from)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
|
|
Object *ob = OBACT(view_layer);
|
|
|
|
|
|
|
|
if (snode->shaderfrom == SNODE_SHADER_OBJECT) {
|
|
|
|
if (ob) {
|
|
|
|
*r_from = &ob->id;
|
|
|
|
if (ob->type == OB_LAMP) {
|
|
|
|
*r_id = ob->data;
|
|
|
|
*r_ntree = ((Light *)ob->data)->nodetree;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Material *ma = give_current_material(ob, ob->actcol);
|
|
|
|
if (ma) {
|
|
|
|
*r_id = &ma->id;
|
|
|
|
*r_ntree = ma->nodetree;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-11 16:51:04 +09:00
|
|
|
#ifdef WITH_FREESTYLE
|
2019-04-17 06:17:24 +02:00
|
|
|
else if (snode->shaderfrom == SNODE_SHADER_LINESTYLE) {
|
|
|
|
FreestyleLineStyle *linestyle = BKE_linestyle_active_from_view_layer(view_layer);
|
|
|
|
if (linestyle) {
|
|
|
|
*r_from = NULL;
|
|
|
|
*r_id = &linestyle->id;
|
|
|
|
*r_ntree = linestyle->nodetree;
|
|
|
|
}
|
|
|
|
}
|
2014-07-11 16:51:04 +09:00
|
|
|
#endif
|
2019-04-17 06:17:24 +02:00
|
|
|
else { /* SNODE_SHADER_WORLD */
|
|
|
|
if (scene->world) {
|
|
|
|
*r_from = NULL;
|
|
|
|
*r_id = &scene->world->id;
|
|
|
|
*r_ntree = scene->world->nodetree;
|
|
|
|
}
|
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
|
|
|
|
Remove Blender Internal and legacy viewport from Blender 2.8.
Brecht authored this commit, but he gave me the honours to actually
do it. Here it goes; Blender Internal. Bye bye, you did great!
* Point density, voxel data, ocean, environment map textures were removed,
as these only worked within BI rendering. Note that the ocean modifier
and the Cycles point density shader node continue to work.
* Dynamic paint using material shading was removed, as this only worked
with BI. If we ever wanted to support this again probably it should go
through the baking API.
* GPU shader export through the Python API was removed. This only worked
for the old BI GLSL shaders, which no longer exists. Doing something
similar for Eevee would be significantly more complicated because it
uses a lot of multiplass rendering and logic outside the shader, it's
probably impractical.
* Collada material import / export code is mostly gone, as it only worked
for BI materials. We need to add Cycles / Eevee material support at some
point.
* The mesh noise operator was removed since it only worked with BI
material texture slots. A displacement modifier can be used instead.
* The delete texture paint slot operator was removed since it only worked
for BI material texture slots. Could be added back with node support.
* Not all legacy viewport features are supported in the new viewport, but
their code was removed. If we need to bring anything back we can look at
older git revisions.
* There is some legacy viewport code that I could not remove yet, and some
that I probably missed.
* Shader node execution code was left mostly intact, even though it is not
used anywhere now. We may eventually use this to replace the texture
nodes with Cycles / Eevee shader nodes.
* The Cycles Bake panel now includes settings for baking multires normal
and displacement maps. The underlying code needs to be merged properly,
and we plan to add back support for multires AO baking and add support
to Cycles baking for features like vertex color, displacement, and other
missing baking features.
* This commit removes DNA and the Python API for BI material, lamp, world
and scene settings. This breaks a lot of addons.
* There is more DNA that can be removed or renamed, where Cycles or Eevee
are reusing some old BI properties but the names are not really correct
anymore.
* Texture slots for materials, lamps and world were removed. They remain
for brushes, particles and freestyle linestyles.
* 'BLENDER_RENDER' remains in the COMPAT_ENGINES of UI panels. Cycles and
other renderers use this to find all panels to show, minus a few panels
that they have their own replacement for.
2018-04-19 17:34:44 +02:00
|
|
|
static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCallback func)
|
2011-11-07 22:14:48 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
func(calldata, NODE_CLASS_INPUT, N_("Input"));
|
|
|
|
func(calldata, NODE_CLASS_OUTPUT, N_("Output"));
|
|
|
|
func(calldata, NODE_CLASS_SHADER, N_("Shader"));
|
|
|
|
func(calldata, NODE_CLASS_TEXTURE, N_("Texture"));
|
|
|
|
func(calldata, NODE_CLASS_OP_COLOR, N_("Color"));
|
|
|
|
func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector"));
|
|
|
|
func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor"));
|
|
|
|
func(calldata, NODE_CLASS_SCRIPT, N_("Script"));
|
|
|
|
func(calldata, NODE_CLASS_GROUP, N_("Group"));
|
|
|
|
func(calldata, NODE_CLASS_INTERFACE, N_("Interface"));
|
|
|
|
func(calldata, NODE_CLASS_LAYOUT, N_("Layout"));
|
2011-11-07 22:14:48 +00:00
|
|
|
}
|
|
|
|
|
2012-02-27 17:38:16 +00:00
|
|
|
static void localize(bNodeTree *localtree, bNodeTree *UNUSED(ntree))
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNode *node, *node_next;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* replace muted nodes and reroute nodes by internal links */
|
|
|
|
for (node = localtree->nodes.first; node; node = node_next) {
|
|
|
|
node_next = node->next;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) {
|
|
|
|
nodeInternalRelink(localtree, node);
|
|
|
|
ntreeFreeLocalNode(localtree, node);
|
|
|
|
}
|
|
|
|
}
|
2012-02-27 17:38:16 +00:00
|
|
|
}
|
|
|
|
|
2013-03-22 10:34:52 +00:00
|
|
|
static void local_sync(bNodeTree *localtree, bNodeTree *ntree)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
BKE_node_preview_sync_tree(ntree, localtree);
|
2013-03-22 10:34:52 +00:00
|
|
|
}
|
|
|
|
|
2018-06-11 15:40:37 +02:00
|
|
|
static void local_merge(Main *UNUSED(bmain), bNodeTree *localtree, bNodeTree *ntree)
|
2013-03-22 10:34:52 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
BKE_node_preview_merge_tree(ntree, localtree, true);
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
|
|
|
|
2011-10-19 17:08:35 +00:00
|
|
|
static void update(bNodeTree *ntree)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
ntreeSetOutput(ntree);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
ntree_update_reroute_nodes(ntree);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (ntree->update & NTREE_UPDATE_NODES) {
|
|
|
|
/* clean up preview cache, in case nodes have been removed */
|
|
|
|
BKE_node_preview_remove_unused(ntree);
|
|
|
|
}
|
2011-10-19 17:08:35 +00:00
|
|
|
}
|
|
|
|
|
2019-06-22 15:36:29 +02:00
|
|
|
static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
|
|
|
|
{
|
|
|
|
/* Can't connect shader into other socket types, other way around is fine
|
|
|
|
* since it will be interpreted as emission. */
|
|
|
|
if (link->fromsock->type == SOCK_SHADER) {
|
|
|
|
return (link->tosock->type == SOCK_SHADER);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
bNodeTreeType *ntreeType_Shader;
|
|
|
|
|
2013-03-18 18:25:05 +00:00
|
|
|
void register_node_tree_type_sh(void)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNodeTreeType *tt = ntreeType_Shader = MEM_callocN(sizeof(bNodeTreeType),
|
|
|
|
"shader node tree type");
|
|
|
|
|
|
|
|
tt->type = NTREE_SHADER;
|
|
|
|
strcpy(tt->idname, "ShaderNodeTree");
|
|
|
|
strcpy(tt->ui_name, N_("Shader Editor"));
|
|
|
|
tt->ui_icon = 0; /* defined in drawnode.c */
|
|
|
|
strcpy(tt->ui_description, N_("Shader nodes"));
|
|
|
|
|
|
|
|
tt->foreach_nodeclass = foreach_nodeclass;
|
|
|
|
tt->localize = localize;
|
|
|
|
tt->local_sync = local_sync;
|
|
|
|
tt->local_merge = local_merge;
|
|
|
|
tt->update = update;
|
|
|
|
tt->poll = shader_tree_poll;
|
|
|
|
tt->get_from_context = shader_get_from_context;
|
2019-06-22 15:36:29 +02:00
|
|
|
tt->validate_link = shader_validate_link;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
tt->ext.srna = &RNA_ShaderNodeTree;
|
|
|
|
|
|
|
|
ntreeTypeAdd(tt);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
|
|
|
|
/* GPU material from shader nodes */
|
|
|
|
|
2016-06-14 11:31:00 +02:00
|
|
|
static void ntree_shader_link_builtin_normal(bNodeTree *ntree,
|
|
|
|
bNode *node_from,
|
|
|
|
bNodeSocket *socket_from,
|
|
|
|
bNode *displacement_node,
|
|
|
|
bNodeSocket *displacement_socket);
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static bNodeSocket *ntree_shader_node_find_input(bNode *node, const char *identifier);
|
2019-03-05 17:24:31 +01:00
|
|
|
|
|
|
|
static bNode *ntree_group_output_node(bNodeTree *ntree);
|
|
|
|
|
|
|
|
static bNode *ntree_shader_relink_output_from_group(bNodeTree *ntree,
|
|
|
|
bNode *group_node,
|
|
|
|
bNode *sh_output_node,
|
|
|
|
int target)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
int i;
|
|
|
|
bNodeTree *group_ntree = (bNodeTree *)group_node->id;
|
|
|
|
|
|
|
|
int sock_len = BLI_listbase_count(&sh_output_node->inputs);
|
|
|
|
bNodeSocket **group_surface_sockets = BLI_array_alloca(group_surface_sockets, sock_len);
|
|
|
|
|
|
|
|
/* Create output sockets to plug output connection to. */
|
|
|
|
i = 0;
|
2019-09-08 00:12:26 +10:00
|
|
|
for (bNodeSocket *sock = sh_output_node->inputs.first; sock; sock = sock->next, i++) {
|
2019-04-17 06:17:24 +02:00
|
|
|
group_surface_sockets[i] = ntreeAddSocketInterface(
|
|
|
|
group_ntree, SOCK_OUT, sock->typeinfo->idname, sock->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
bNode *group_output_node = ntree_group_output_node(group_ntree);
|
|
|
|
|
|
|
|
/* If no group output node is present, we need to create one. */
|
|
|
|
if (group_output_node == NULL) {
|
|
|
|
group_output_node = nodeAddStaticNode(NULL, group_ntree, NODE_GROUP_OUTPUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Need to update tree so all node instances nodes gets proper sockets. */
|
2019-04-20 20:25:22 +02:00
|
|
|
node_group_update(ntree, group_node);
|
|
|
|
node_group_output_update(group_ntree, group_output_node);
|
2019-04-17 06:17:24 +02:00
|
|
|
ntreeUpdateTree(G.main, group_ntree);
|
|
|
|
|
|
|
|
/* Remove other shader output nodes so that only the new one can be selected as active. */
|
|
|
|
for (bNode *node = ntree->nodes.first; node; node = node->next) {
|
|
|
|
if (ELEM(node->type, SH_NODE_OUTPUT_MATERIAL, SH_NODE_OUTPUT_WORLD, SH_NODE_OUTPUT_LIGHT)) {
|
|
|
|
ntreeFreeLocalNode(ntree, node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create new shader output node outside the group. */
|
|
|
|
bNode *new_output_node = nodeAddStaticNode(NULL, ntree, sh_output_node->type);
|
|
|
|
new_output_node->custom1 = target;
|
|
|
|
|
|
|
|
i = 0;
|
2019-09-08 00:12:26 +10:00
|
|
|
for (bNodeSocket *sock = sh_output_node->inputs.first; sock; sock = sock->next, i++) {
|
2019-04-17 06:17:24 +02:00
|
|
|
if (sock->link != NULL) {
|
|
|
|
/* Link the shader output node incoming link to the group output sockets */
|
|
|
|
bNodeSocket *group_output_node_surface_input_sock = nodeFindSocket(
|
|
|
|
group_output_node, SOCK_IN, group_surface_sockets[i]->identifier);
|
|
|
|
nodeAddLink(group_ntree,
|
|
|
|
sock->link->fromnode,
|
|
|
|
sock->link->fromsock,
|
|
|
|
group_output_node,
|
|
|
|
group_output_node_surface_input_sock);
|
|
|
|
|
|
|
|
/* Link the group output sockets to the new shader output node. */
|
|
|
|
bNodeSocket *group_node_surface_output = nodeFindSocket(
|
|
|
|
group_node, SOCK_OUT, group_surface_sockets[i]->identifier);
|
|
|
|
bNodeSocket *output_node_surface_input = ntree_shader_node_find_input(new_output_node,
|
|
|
|
sock->name);
|
|
|
|
|
|
|
|
nodeAddLink(ntree,
|
|
|
|
group_node,
|
|
|
|
group_node_surface_output,
|
|
|
|
new_output_node,
|
|
|
|
output_node_surface_input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ntreeUpdateTree(G.main, group_ntree);
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
|
|
|
|
|
|
|
return new_output_node;
|
2019-03-05 17:24:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static bNode *ntree_shader_output_node_from_group(bNodeTree *ntree, int target)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNode *output_node = NULL;
|
|
|
|
|
|
|
|
/* Search if node groups do not contain valid output nodes (recursively). */
|
|
|
|
for (bNode *node = ntree->nodes.first; node; node = node->next) {
|
|
|
|
if (!ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (node->id != NULL) {
|
|
|
|
output_node = ntree_shader_output_node_from_group((bNodeTree *)node->id, target);
|
|
|
|
|
|
|
|
if (output_node == NULL) {
|
|
|
|
output_node = ntreeShaderOutputNode((bNodeTree *)node->id, target);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (output_node != NULL) {
|
|
|
|
/* Output is inside this group node. Create relink to make the output outside the group. */
|
|
|
|
output_node = ntree_shader_relink_output_from_group(ntree, node, output_node, target);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return output_node;
|
2019-03-05 17:24:31 +01:00
|
|
|
}
|
|
|
|
|
2016-05-20 14:16:54 +02:00
|
|
|
/* Find an output node of the shader tree.
|
|
|
|
*
|
|
|
|
* NOTE: it will only return output which is NOT in the group, which isn't how
|
|
|
|
* render engines works but it's how the GPU shader compilation works. This we
|
|
|
|
* can change in the future and make it a generic function, but for now it stays
|
|
|
|
* private here.
|
|
|
|
*/
|
2018-07-05 12:44:15 +02:00
|
|
|
bNode *ntreeShaderOutputNode(bNodeTree *ntree, int target)
|
2016-05-20 14:16:54 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
/* Make sure we only have single node tagged as output. */
|
|
|
|
ntreeSetOutput(ntree);
|
|
|
|
|
|
|
|
/* Find output node that matches type and target. If there are
|
|
|
|
* multiple, we prefer exact target match and active nodes. */
|
|
|
|
bNode *output_node = NULL;
|
|
|
|
|
|
|
|
for (bNode *node = ntree->nodes.first; node; node = node->next) {
|
|
|
|
if (!ELEM(node->type, SH_NODE_OUTPUT_MATERIAL, SH_NODE_OUTPUT_WORLD, SH_NODE_OUTPUT_LIGHT)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node->custom1 == SHD_OUTPUT_ALL) {
|
|
|
|
if (output_node == NULL) {
|
|
|
|
output_node = node;
|
|
|
|
}
|
|
|
|
else if (output_node->custom1 == SHD_OUTPUT_ALL) {
|
|
|
|
if ((node->flag & NODE_DO_OUTPUT) && !(output_node->flag & NODE_DO_OUTPUT)) {
|
|
|
|
output_node = node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (node->custom1 == target) {
|
|
|
|
if (output_node == NULL) {
|
|
|
|
output_node = node;
|
|
|
|
}
|
|
|
|
else if (output_node->custom1 == SHD_OUTPUT_ALL) {
|
|
|
|
output_node = node;
|
|
|
|
}
|
|
|
|
else if ((node->flag & NODE_DO_OUTPUT) && !(output_node->flag & NODE_DO_OUTPUT)) {
|
|
|
|
output_node = node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return output_node;
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
|
|
|
|
2018-11-17 17:08:21 +01:00
|
|
|
/* Find the active output node of a group nodetree.
|
|
|
|
*
|
|
|
|
* Does not return the shading output node but the group output node.
|
|
|
|
*/
|
|
|
|
static bNode *ntree_group_output_node(bNodeTree *ntree)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
/* Make sure we only have single node tagged as output. */
|
|
|
|
ntreeSetOutput(ntree);
|
|
|
|
|
|
|
|
/* Find output node that matches type and target. If there are
|
|
|
|
* multiple, we prefer exact target match and active nodes. */
|
|
|
|
bNode *output_node = NULL;
|
|
|
|
|
|
|
|
for (bNode *node = ntree->nodes.first; node; node = node->next) {
|
|
|
|
if ((node->type == NODE_GROUP_OUTPUT) && (node->flag & NODE_DO_OUTPUT)) {
|
|
|
|
output_node = node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return output_node;
|
2018-11-17 17:08:21 +01:00
|
|
|
}
|
|
|
|
|
2016-05-20 14:16:54 +02:00
|
|
|
/* Find socket with a specified identifier. */
|
2019-04-17 06:17:24 +02:00
|
|
|
static bNodeSocket *ntree_shader_node_find_socket(ListBase *sockets, const char *identifier)
|
2016-05-20 14:16:54 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
for (bNodeSocket *sock = sockets->first; sock != NULL; sock = sock->next) {
|
|
|
|
if (STREQ(sock->identifier, identifier)) {
|
|
|
|
return sock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Find input socket with a specified identifier. */
|
2019-04-17 06:17:24 +02:00
|
|
|
static bNodeSocket *ntree_shader_node_find_input(bNode *node, const char *identifier)
|
2016-05-20 14:16:54 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
return ntree_shader_node_find_socket(&node->inputs, identifier);
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Find output socket with a specified identifier. */
|
2019-04-17 06:17:24 +02:00
|
|
|
static bNodeSocket *ntree_shader_node_find_output(bNode *node, const char *identifier)
|
2016-05-20 14:16:54 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
return ntree_shader_node_find_socket(&node->outputs, identifier);
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
|
|
|
|
2018-11-01 15:06:23 +01:00
|
|
|
static void ntree_shader_unlink_hidden_value_sockets(bNode *group_node, bNodeSocket *isock)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNodeTree *group_ntree = (bNodeTree *)group_node->id;
|
|
|
|
bNode *node;
|
|
|
|
bool removed_link = false;
|
|
|
|
|
|
|
|
for (node = group_ntree->nodes.first; node; node = node->next) {
|
|
|
|
for (bNodeSocket *sock = node->inputs.first; sock; sock = sock->next) {
|
2019-04-22 13:31:31 +10:00
|
|
|
if ((sock->flag & SOCK_HIDE_VALUE) == 0) {
|
2019-04-17 06:17:24 +02:00
|
|
|
continue;
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
/* If socket is linked to a group input node and sockets id match. */
|
|
|
|
if (sock && sock->link && sock->link->fromnode->type == NODE_GROUP_INPUT) {
|
|
|
|
if (STREQ(isock->identifier, sock->link->fromsock->identifier)) {
|
|
|
|
nodeRemLink(group_ntree, sock->link);
|
|
|
|
removed_link = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (removed_link) {
|
|
|
|
ntreeUpdateTree(G.main, group_ntree);
|
|
|
|
}
|
2018-11-01 15:06:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Node groups once expanded looses their input sockets values.
|
|
|
|
* To fix this, link value/rgba nodes into the sockets and copy the group sockets values. */
|
|
|
|
static void ntree_shader_groups_expand_inputs(bNodeTree *localtree)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNode *value_node, *group_node;
|
|
|
|
bNodeSocket *value_socket;
|
|
|
|
bNodeSocketValueVector *src_vector;
|
|
|
|
bNodeSocketValueRGBA *src_rgba, *dst_rgba;
|
|
|
|
bNodeSocketValueFloat *src_float, *dst_float;
|
|
|
|
bNodeSocketValueInt *src_int;
|
|
|
|
bool link_added = false;
|
|
|
|
|
|
|
|
for (group_node = localtree->nodes.first; group_node; group_node = group_node->next) {
|
|
|
|
|
2019-04-22 13:31:31 +10:00
|
|
|
if (!(ELEM(group_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) || group_node->id == NULL) {
|
2019-04-17 06:17:24 +02:00
|
|
|
continue;
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
/* Do it recursively. */
|
|
|
|
ntree_shader_groups_expand_inputs((bNodeTree *)group_node->id);
|
|
|
|
|
|
|
|
bNodeSocket *group_socket = group_node->inputs.first;
|
|
|
|
for (; group_socket; group_socket = group_socket->next) {
|
2019-04-22 13:31:31 +10:00
|
|
|
if (group_socket->link != NULL) {
|
2019-04-17 06:17:24 +02:00
|
|
|
continue;
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
/* Detect the case where an input is plugged into a hidden value socket.
|
|
|
|
* In this case we should just remove the link to trigger the socket default override. */
|
|
|
|
ntree_shader_unlink_hidden_value_sockets(group_node, group_socket);
|
|
|
|
|
|
|
|
switch (group_socket->type) {
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
value_node = nodeAddStaticNode(NULL, localtree, SH_NODE_RGB);
|
|
|
|
value_socket = ntree_shader_node_find_output(value_node, "Color");
|
|
|
|
BLI_assert(value_socket != NULL);
|
|
|
|
src_vector = group_socket->default_value;
|
|
|
|
dst_rgba = value_socket->default_value;
|
|
|
|
copy_v3_v3(dst_rgba->value, src_vector->value);
|
|
|
|
dst_rgba->value[3] = 1.0f; /* should never be read */
|
|
|
|
break;
|
|
|
|
case SOCK_RGBA:
|
|
|
|
value_node = nodeAddStaticNode(NULL, localtree, SH_NODE_RGB);
|
|
|
|
value_socket = ntree_shader_node_find_output(value_node, "Color");
|
|
|
|
BLI_assert(value_socket != NULL);
|
|
|
|
src_rgba = group_socket->default_value;
|
|
|
|
dst_rgba = value_socket->default_value;
|
|
|
|
copy_v4_v4(dst_rgba->value, src_rgba->value);
|
|
|
|
break;
|
|
|
|
case SOCK_INT:
|
|
|
|
/* HACK: Support as float. */
|
|
|
|
value_node = nodeAddStaticNode(NULL, localtree, SH_NODE_VALUE);
|
|
|
|
value_socket = ntree_shader_node_find_output(value_node, "Value");
|
|
|
|
BLI_assert(value_socket != NULL);
|
|
|
|
src_int = group_socket->default_value;
|
|
|
|
dst_float = value_socket->default_value;
|
|
|
|
dst_float->value = (float)(src_int->value);
|
|
|
|
break;
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
value_node = nodeAddStaticNode(NULL, localtree, SH_NODE_VALUE);
|
|
|
|
value_socket = ntree_shader_node_find_output(value_node, "Value");
|
|
|
|
BLI_assert(value_socket != NULL);
|
|
|
|
src_float = group_socket->default_value;
|
|
|
|
dst_float = value_socket->default_value;
|
|
|
|
dst_float->value = src_float->value;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeAddLink(localtree, value_node, value_socket, group_node, group_socket);
|
|
|
|
|
|
|
|
link_added = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (link_added) {
|
|
|
|
ntreeUpdateTree(G.main, localtree);
|
|
|
|
}
|
2018-11-01 15:06:23 +01:00
|
|
|
}
|
|
|
|
|
2016-05-20 14:16:54 +02:00
|
|
|
/* Check whether shader has a displacement.
|
|
|
|
*
|
|
|
|
* Will also return a node and it's socket which is connected to a displacement
|
|
|
|
* output. Additionally, link which is attached to the displacement output is
|
|
|
|
* also returned.
|
|
|
|
*/
|
|
|
|
static bool ntree_shader_has_displacement(bNodeTree *ntree,
|
2018-07-05 17:20:44 +02:00
|
|
|
bNode *output_node,
|
2016-05-20 14:16:54 +02:00
|
|
|
bNode **r_node,
|
|
|
|
bNodeSocket **r_socket,
|
|
|
|
bNodeLink **r_link)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
if (output_node == NULL) {
|
|
|
|
/* We can't have displacement without output node, apparently. */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/* Make sure sockets links pointers are correct. */
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
|
|
|
bNodeSocket *displacement = ntree_shader_node_find_input(output_node, "Displacement");
|
|
|
|
|
|
|
|
if (displacement == NULL) {
|
|
|
|
/* Non-cycles node is used as an output. */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (displacement->link != NULL) {
|
|
|
|
*r_node = displacement->link->fromnode;
|
|
|
|
*r_socket = displacement->link->fromsock;
|
|
|
|
*r_link = displacement->link;
|
|
|
|
}
|
|
|
|
return displacement->link != NULL;
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
|
|
|
|
2019-06-26 12:03:15 +02:00
|
|
|
static void ntree_shader_relink_node_normal(bNodeTree *ntree,
|
2016-06-14 11:31:00 +02:00
|
|
|
bNode *node,
|
|
|
|
bNode *node_from,
|
|
|
|
bNodeSocket *socket_from)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
/* TODO(sergey): Can we do something smarter here than just a name-based
|
|
|
|
* matching?
|
|
|
|
*/
|
2019-06-26 12:03:15 +02:00
|
|
|
for (bNodeSocket *sock = node->inputs.first; sock; sock = sock->next) {
|
|
|
|
if (STREQ(sock->identifier, "Normal") && sock->link == NULL) {
|
|
|
|
/* It's a normal input and nothing is connected to it. */
|
|
|
|
nodeAddLink(ntree, node_from, socket_from, node, sock);
|
|
|
|
}
|
|
|
|
else if (sock->link) {
|
|
|
|
bNodeLink *link = sock->link;
|
|
|
|
if (ELEM(link->fromnode->type, SH_NODE_NEW_GEOMETRY, SH_NODE_TEX_COORD) &&
|
|
|
|
STREQ(link->fromsock->identifier, "Normal")) {
|
|
|
|
/* Linked to a geometry node normal output. */
|
|
|
|
nodeAddLink(ntree, node_from, socket_from, node, sock);
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2016-06-14 11:31:00 +02:00
|
|
|
}
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static void ntree_shader_link_builtin_group_normal(bNodeTree *ntree,
|
|
|
|
bNode *group_node,
|
|
|
|
bNode *node_from,
|
|
|
|
bNodeSocket *socket_from,
|
|
|
|
bNode *displacement_node,
|
|
|
|
bNodeSocket *displacement_socket)
|
2016-06-14 11:31:00 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNodeTree *group_ntree = (bNodeTree *)group_node->id;
|
|
|
|
/* Create input socket to plug displacement connection to. */
|
|
|
|
bNodeSocket *group_normal_socket = ntreeAddSocketInterface(
|
|
|
|
group_ntree, SOCK_IN, "NodeSocketVector", "Normal");
|
|
|
|
/* Need to update tree so all node instances nodes gets proper sockets. */
|
|
|
|
bNode *group_input_node = ntreeFindType(group_ntree, NODE_GROUP_INPUT);
|
2019-04-20 20:25:22 +02:00
|
|
|
node_group_update(ntree, group_node);
|
2019-04-22 13:31:31 +10:00
|
|
|
if (group_input_node) {
|
2019-04-20 20:25:22 +02:00
|
|
|
node_group_input_update(group_ntree, group_input_node);
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
ntreeUpdateTree(G.main, group_ntree);
|
|
|
|
/* Assumes sockets are always added at the end. */
|
|
|
|
bNodeSocket *group_node_normal_socket = group_node->inputs.last;
|
|
|
|
if (displacement_node == group_node) {
|
2019-06-26 12:03:15 +02:00
|
|
|
/* This should never happen as all displacement nodes are duplicated and tagged. */
|
|
|
|
BLI_assert(0);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
else if (group_input_node) {
|
|
|
|
/* Connect group node normal input. */
|
|
|
|
nodeAddLink(ntree, node_from, socket_from, group_node, group_node_normal_socket);
|
|
|
|
BLI_assert(group_input_node != NULL);
|
|
|
|
bNodeSocket *group_input_node_normal_socket = nodeFindSocket(
|
|
|
|
group_input_node, SOCK_OUT, group_normal_socket->identifier);
|
|
|
|
BLI_assert(group_input_node_normal_socket != NULL);
|
|
|
|
/* Relink normals inside of the instanced tree. */
|
|
|
|
ntree_shader_link_builtin_normal(group_ntree,
|
|
|
|
group_input_node,
|
|
|
|
group_input_node_normal_socket,
|
|
|
|
displacement_node,
|
|
|
|
displacement_socket);
|
|
|
|
ntreeUpdateTree(G.main, group_ntree);
|
|
|
|
}
|
2016-06-14 11:31:00 +02:00
|
|
|
}
|
|
|
|
|
2016-05-20 14:16:54 +02:00
|
|
|
/* Use specified node and socket as an input for unconnected normal sockets. */
|
|
|
|
static void ntree_shader_link_builtin_normal(bNodeTree *ntree,
|
|
|
|
bNode *node_from,
|
2016-06-14 11:31:00 +02:00
|
|
|
bNodeSocket *socket_from,
|
|
|
|
bNode *displacement_node,
|
|
|
|
bNodeSocket *displacement_socket)
|
2016-05-20 14:16:54 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
for (bNode *node = ntree->nodes.first; node != NULL; node = node->next) {
|
|
|
|
if (node == node_from) {
|
|
|
|
/* Don't connect node itself! */
|
|
|
|
continue;
|
|
|
|
}
|
2019-06-26 12:03:15 +02:00
|
|
|
if (node->tmp_flag == -2) {
|
|
|
|
/* This node is used inside the displacement tree. Skip to avoid cycles. */
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
if ((ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) && node->id) {
|
|
|
|
/* Special re-linking for group nodes. */
|
|
|
|
ntree_shader_link_builtin_group_normal(
|
|
|
|
ntree, node, node_from, socket_from, displacement_node, displacement_socket);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {
|
|
|
|
/* Group inputs and outputs needs nothing special. */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ntree_shader_relink_node_normal(ntree, node, node_from, socket_from);
|
|
|
|
}
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
|
|
|
|
2019-06-26 12:03:15 +02:00
|
|
|
static void ntree_shader_bypass_bump_link(bNodeTree *ntree, bNode *bump_node, bNodeLink *bump_link)
|
|
|
|
{
|
|
|
|
/* Bypass bump nodes. This replicates cycles "implicit" behavior. */
|
|
|
|
bNodeSocket *bump_normal_input = ntree_shader_node_find_input(bump_node, "Normal");
|
|
|
|
bNode *fromnode;
|
|
|
|
bNodeSocket *fromsock;
|
|
|
|
/* Default to builtin normals if there is no link. */
|
|
|
|
if (bump_normal_input->link) {
|
|
|
|
fromsock = bump_normal_input->link->fromsock;
|
|
|
|
fromnode = bump_normal_input->link->fromnode;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fromnode = nodeAddStaticNode(NULL, ntree, SH_NODE_NEW_GEOMETRY);
|
|
|
|
fromsock = ntree_shader_node_find_output(fromnode, "Normal");
|
|
|
|
}
|
|
|
|
/* Bypass the bump node by creating a link between the previous and next node. */
|
|
|
|
nodeAddLink(ntree, fromnode, fromsock, bump_link->tonode, bump_link->tosock);
|
|
|
|
nodeRemLink(ntree, bump_link);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ntree_shader_bypass_tagged_bump_nodes(bNodeTree *ntree)
|
|
|
|
{
|
|
|
|
/* Bypass bump links inside copied nodes */
|
|
|
|
bNodeLink *link, *link_next;
|
|
|
|
for (link = ntree->links.first; link; link = link_next) {
|
|
|
|
/* link might be freed by ntree_shader_bypass_bump_link. */
|
|
|
|
link_next = link->next;
|
|
|
|
bNode *node = link->fromnode;
|
|
|
|
/* If node is a copy. */
|
|
|
|
if (node->tmp_flag == -2 && node->type == SH_NODE_BUMP) {
|
|
|
|
ntree_shader_bypass_bump_link(ntree, node, link);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Do the same inside nodegroups. */
|
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
|
|
/* If node is a copy. */
|
|
|
|
if (node->tmp_flag == -2 && ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id) {
|
|
|
|
bNodeTree *group_ntree = (bNodeTree *)node->id;
|
|
|
|
/* Tag all nodes inside this group as copies. */
|
|
|
|
LISTBASE_FOREACH (bNode *, group_node, &group_ntree->nodes) {
|
|
|
|
group_node->tmp_flag = -2;
|
|
|
|
}
|
|
|
|
/* Recursive. */
|
|
|
|
ntree_shader_bypass_tagged_bump_nodes(group_ntree);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ntree_branch_count_and_tag_nodes(bNode *fromnode,
|
|
|
|
bNode *tonode,
|
|
|
|
void *userdata,
|
|
|
|
const bool UNUSED(reversed))
|
|
|
|
{
|
|
|
|
int *node_count = (int *)userdata;
|
|
|
|
if (fromnode->tmp_flag == -1) {
|
|
|
|
fromnode->tmp_flag = *node_count;
|
|
|
|
(*node_count)++;
|
|
|
|
}
|
|
|
|
if (tonode->tmp_flag == -1) {
|
|
|
|
tonode->tmp_flag = *node_count;
|
|
|
|
(*node_count)++;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-19 20:33:17 +02:00
|
|
|
/* Create a copy of a branch starting from a given node.
|
|
|
|
* callback is executed once for every copied node.
|
|
|
|
* Returns input node copy. */
|
|
|
|
static bNode *ntree_shader_copy_branch(bNodeTree *ntree,
|
|
|
|
bNode *start_node,
|
|
|
|
void (*callback)(bNode *node, int user_data),
|
|
|
|
int user_data)
|
2019-06-26 12:03:15 +02:00
|
|
|
{
|
|
|
|
/* Init tmp flag. */
|
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
|
|
node->tmp_flag = -1;
|
|
|
|
}
|
|
|
|
/* Count and tag all nodes inside the displacement branch of the tree. */
|
2019-08-19 20:33:17 +02:00
|
|
|
start_node->tmp_flag = 0;
|
2019-06-26 12:03:15 +02:00
|
|
|
int node_count = 1;
|
2019-08-19 20:33:17 +02:00
|
|
|
nodeChainIter(ntree, start_node, ntree_branch_count_and_tag_nodes, &node_count, true);
|
2019-06-26 12:03:15 +02:00
|
|
|
/* Make a full copy of the branch */
|
|
|
|
bNode **nodes_copy = MEM_mallocN(sizeof(bNode *) * node_count, __func__);
|
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
|
|
if (node->tmp_flag >= 0) {
|
|
|
|
int id = node->tmp_flag;
|
|
|
|
nodes_copy[id] = BKE_node_copy_ex(ntree, node, LIB_ID_CREATE_NO_USER_REFCOUNT);
|
|
|
|
nodes_copy[id]->tmp_flag = -2; /* Copy */
|
|
|
|
if (ELEM(nodes_copy[id]->type, NODE_GROUP, NODE_CUSTOM_GROUP) && nodes_copy[id]->id) {
|
|
|
|
nodes_copy[id]->id = (ID *)ntreeLocalize((bNodeTree *)nodes_copy[id]->id);
|
|
|
|
}
|
|
|
|
/* Make sure to clear all sockets links as they are invalid. */
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &nodes_copy[id]->inputs) {
|
|
|
|
sock->link = NULL;
|
|
|
|
}
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &nodes_copy[id]->outputs) {
|
|
|
|
sock->link = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Recreate links between copied nodes. */
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
|
|
|
if (link->fromnode->tmp_flag >= 0 && link->tonode->tmp_flag >= 0) {
|
|
|
|
bNode *fromnode = nodes_copy[link->fromnode->tmp_flag];
|
|
|
|
bNode *tonode = nodes_copy[link->tonode->tmp_flag];
|
|
|
|
bNodeSocket *fromsock = ntree_shader_node_find_output(fromnode, link->fromsock->identifier);
|
|
|
|
bNodeSocket *tosock = ntree_shader_node_find_input(tonode, link->tosock->identifier);
|
|
|
|
nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
|
|
|
|
}
|
|
|
|
}
|
2019-08-19 20:33:17 +02:00
|
|
|
/* Per node callback. */
|
|
|
|
if (callback) {
|
|
|
|
for (int i = 0; i < node_count; i++) {
|
|
|
|
callback(nodes_copy[i], user_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bNode *start_node_copy = nodes_copy[start_node->tmp_flag];
|
|
|
|
MEM_freeN(nodes_copy);
|
|
|
|
return start_node_copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ntree_shader_copy_branch_displacement(bNodeTree *ntree,
|
|
|
|
bNode *displacement_node,
|
|
|
|
bNodeSocket *displacement_socket,
|
|
|
|
bNodeLink *displacement_link)
|
|
|
|
{
|
2019-06-26 12:03:15 +02:00
|
|
|
/* Replace displacement socket/node/link. */
|
|
|
|
bNode *tonode = displacement_link->tonode;
|
|
|
|
bNodeSocket *tosock = displacement_link->tosock;
|
2019-08-19 20:33:17 +02:00
|
|
|
displacement_node = ntree_shader_copy_branch(ntree, displacement_node, NULL, 0);
|
2019-06-26 12:03:15 +02:00
|
|
|
displacement_socket = ntree_shader_node_find_output(displacement_node,
|
|
|
|
displacement_socket->identifier);
|
|
|
|
nodeRemLink(ntree, displacement_link);
|
|
|
|
nodeAddLink(ntree, displacement_node, displacement_socket, tonode, tosock);
|
|
|
|
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
|
|
|
}
|
|
|
|
|
2016-05-20 14:16:54 +02:00
|
|
|
/* Re-link displacement output to unconnected normal sockets via bump node.
|
|
|
|
* This way material with have proper displacement in the viewport.
|
|
|
|
*/
|
2018-07-05 17:20:44 +02:00
|
|
|
static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_node)
|
2016-05-20 14:16:54 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNode *displacement_node;
|
|
|
|
bNodeSocket *displacement_socket;
|
|
|
|
bNodeLink *displacement_link;
|
|
|
|
if (!ntree_shader_has_displacement(
|
|
|
|
ntree, output_node, &displacement_node, &displacement_socket, &displacement_link)) {
|
|
|
|
/* There is no displacement output connected, nothing to re-link. */
|
|
|
|
return;
|
|
|
|
}
|
2019-06-26 12:03:15 +02:00
|
|
|
|
2019-08-01 13:53:25 +10:00
|
|
|
/* Copy the whole displacement branch to avoid cyclic dependency
|
2019-06-26 12:03:15 +02:00
|
|
|
* and issue when bypassing bump nodes. */
|
|
|
|
ntree_shader_copy_branch_displacement(
|
|
|
|
ntree, displacement_node, displacement_socket, displacement_link);
|
|
|
|
/* Bypass bump nodes inside the copied branch to mimic cycles behavior. */
|
|
|
|
ntree_shader_bypass_tagged_bump_nodes(ntree);
|
|
|
|
|
|
|
|
/* Displacement Node may have changed because of branch copy and bump bypass. */
|
|
|
|
ntree_shader_has_displacement(
|
|
|
|
ntree, output_node, &displacement_node, &displacement_socket, &displacement_link);
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* We have to disconnect displacement output socket, otherwise we'll have
|
|
|
|
* cycles in the Cycles material :)
|
|
|
|
*/
|
|
|
|
nodeRemLink(ntree, displacement_link);
|
|
|
|
|
|
|
|
/* Convert displacement vector to bump height. */
|
Shading: Add more operators to Vector Math node.
Add Multiply, Divide, Project, Reflect, Distance, Length, Scale, Snap,
Floor, Ceil, Modulo, Fraction, Absolute, Minimum, and Maximum operators
to the Vector Math node. The Value output has been removed from operators
whose output is a vector, and the other way around. All of those removals
has been handled properly in versioning code.
The patch doesn't include tests for the new operators. Tests will be added
in a later patch.
Reviewers: brecht, JacquesLucke
Differential Revision: https://developer.blender.org/D5523
2019-08-21 19:36:33 +02:00
|
|
|
bNode *dot_node = nodeAddStaticNode(NULL, ntree, SH_NODE_VECTOR_MATH);
|
2019-04-17 06:17:24 +02:00
|
|
|
bNode *geo_node = nodeAddStaticNode(NULL, ntree, SH_NODE_NEW_GEOMETRY);
|
2019-06-26 12:03:15 +02:00
|
|
|
bNodeSocket *normal_socket = ntree_shader_node_find_output(geo_node, "Normal");
|
2019-09-03 17:45:50 +02:00
|
|
|
bNodeSocket *dot_input1 = dot_node->inputs.first;
|
|
|
|
bNodeSocket *dot_input2 = dot_input1->next;
|
|
|
|
dot_node->custom1 = NODE_VECTOR_MATH_DOT_PRODUCT;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-09-03 17:45:50 +02:00
|
|
|
nodeAddLink(ntree, displacement_node, displacement_socket, dot_node, dot_input1);
|
|
|
|
nodeAddLink(ntree, geo_node, normal_socket, dot_node, dot_input2);
|
2019-04-17 06:17:24 +02:00
|
|
|
displacement_node = dot_node;
|
|
|
|
displacement_socket = ntree_shader_node_find_output(dot_node, "Value");
|
|
|
|
|
|
|
|
/* We can't connect displacement to normal directly, use bump node for that
|
|
|
|
* and hope that it gives good enough approximation.
|
|
|
|
*/
|
|
|
|
bNode *bump_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BUMP);
|
|
|
|
bNodeSocket *bump_input_socket = ntree_shader_node_find_input(bump_node, "Height");
|
|
|
|
bNodeSocket *bump_output_socket = ntree_shader_node_find_output(bump_node, "Normal");
|
|
|
|
BLI_assert(bump_input_socket != NULL);
|
|
|
|
BLI_assert(bump_output_socket != NULL);
|
|
|
|
/* Connect bump node to where displacement output was originally
|
|
|
|
* connected to.
|
|
|
|
*/
|
|
|
|
nodeAddLink(ntree, displacement_node, displacement_socket, bump_node, bump_input_socket);
|
2019-06-26 12:03:15 +02:00
|
|
|
|
|
|
|
/* Tag as part of the new displacmeent tree. */
|
|
|
|
dot_node->tmp_flag = -2;
|
|
|
|
geo_node->tmp_flag = -2;
|
|
|
|
bump_node->tmp_flag = -2;
|
|
|
|
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
|
|
|
|
|
|
|
/* Connect all free-standing Normal inputs and relink geometry/coordinate nodes. */
|
2019-04-17 06:17:24 +02:00
|
|
|
ntree_shader_link_builtin_normal(
|
|
|
|
ntree, bump_node, bump_output_socket, displacement_node, displacement_socket);
|
|
|
|
/* We modified the tree, it needs to be updated now. */
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
|
|
|
|
2019-08-19 20:33:17 +02:00
|
|
|
static void node_tag_branch_as_derivative(bNode *node, int dx)
|
|
|
|
{
|
|
|
|
if (dx) {
|
|
|
|
node->branch_tag = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
node->branch_tag = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ntree_shader_bump_branches(bNode *UNUSED(fromnode),
|
|
|
|
bNode *tonode,
|
|
|
|
void *userdata,
|
|
|
|
const bool UNUSED(reversed))
|
|
|
|
{
|
|
|
|
bNodeTree *ntree = (bNodeTree *)userdata;
|
|
|
|
|
|
|
|
if (tonode->type == SH_NODE_BUMP) {
|
|
|
|
bNodeSocket *height_dx_sock, *height_dy_sock, *bump_socket, *bump_dx_socket, *bump_dy_socket;
|
|
|
|
bNode *bump = tonode;
|
|
|
|
bump_socket = ntree_shader_node_find_input(bump, "Height");
|
|
|
|
bump_dx_socket = ntree_shader_node_find_input(bump, "Height_dx");
|
|
|
|
bump_dy_socket = ntree_shader_node_find_input(bump, "Height_dy");
|
|
|
|
if (bump_dx_socket->link) {
|
|
|
|
/* Avoid reconnecting the same bump twice. */
|
|
|
|
}
|
|
|
|
else if (bump_socket && bump_socket->link) {
|
|
|
|
bNodeLink *link = bump_socket->link;
|
|
|
|
bNode *height = link->fromnode;
|
|
|
|
bNode *height_dx = ntree_shader_copy_branch(ntree, height, node_tag_branch_as_derivative, 1);
|
|
|
|
bNode *height_dy = ntree_shader_copy_branch(ntree, height, node_tag_branch_as_derivative, 0);
|
|
|
|
height_dx_sock = ntree_shader_node_find_output(height_dx, link->fromsock->identifier);
|
|
|
|
height_dy_sock = ntree_shader_node_find_output(height_dy, link->fromsock->identifier);
|
|
|
|
nodeAddLink(ntree, height_dx, height_dx_sock, bump, bump_dx_socket);
|
|
|
|
nodeAddLink(ntree, height_dy, height_dy_sock, bump, bump_dy_socket);
|
|
|
|
/* We could end iter here, but other bump node could be plugged into other input sockets. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static bool ntree_tag_bsdf_cb(bNode *fromnode,
|
|
|
|
bNode *UNUSED(tonode),
|
|
|
|
void *userdata,
|
|
|
|
const bool UNUSED(reversed))
|
2017-07-15 16:09:44 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
/* Don't evaluate nodes more than once. */
|
|
|
|
if (fromnode->tmp_flag) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
fromnode->tmp_flag = 1;
|
|
|
|
|
|
|
|
switch (fromnode->type) {
|
|
|
|
case NODE_GROUP:
|
|
|
|
case NODE_CUSTOM_GROUP:
|
|
|
|
/* Recursive */
|
|
|
|
if (fromnode->id != NULL) {
|
|
|
|
bNodeTree *ntree = (bNodeTree *)fromnode->id;
|
|
|
|
bNode *group_output = ntree_group_output_node(ntree);
|
|
|
|
ntree_shader_tag_nodes(ntree, group_output, (nTreeTags *)userdata);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SH_NODE_BSDF_ANISOTROPIC:
|
|
|
|
case SH_NODE_EEVEE_SPECULAR:
|
|
|
|
case SH_NODE_BSDF_GLOSSY:
|
|
|
|
case SH_NODE_BSDF_GLASS:
|
|
|
|
fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id;
|
|
|
|
((nTreeTags *)userdata)->ssr_id += 1;
|
|
|
|
break;
|
|
|
|
case SH_NODE_SUBSURFACE_SCATTERING:
|
|
|
|
fromnode->sss_id = ((nTreeTags *)userdata)->sss_id;
|
|
|
|
((nTreeTags *)userdata)->sss_id += 1;
|
|
|
|
break;
|
|
|
|
case SH_NODE_BSDF_PRINCIPLED:
|
|
|
|
fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id;
|
|
|
|
fromnode->sss_id = ((nTreeTags *)userdata)->sss_id;
|
|
|
|
((nTreeTags *)userdata)->sss_id += 1;
|
|
|
|
((nTreeTags *)userdata)->ssr_id += 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* We could return false here but since we
|
|
|
|
* allow the use of Closure as RGBA, we can have
|
|
|
|
* Bsdf nodes linked to other Bsdf nodes. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2017-07-15 16:09:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* EEVEE: Scan the ntree to set the Screen Space Reflection
|
2018-12-03 17:19:04 +01:00
|
|
|
* layer id of every specular node AND the Subsurface Scattering id of every SSS node.
|
2017-07-15 16:09:44 +02:00
|
|
|
*/
|
2018-12-03 17:19:04 +01:00
|
|
|
void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags)
|
2017-07-15 16:09:44 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
if (output_node == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Make sure sockets links pointers are correct. */
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
|
|
|
|
|
|
|
/* Reset visit flag. */
|
|
|
|
for (bNode *node = ntree->nodes.first; node; node = node->next) {
|
|
|
|
node->tmp_flag = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeChainIter(ntree, output_node, ntree_tag_bsdf_cb, tags, true);
|
2017-11-13 21:56:49 +01:00
|
|
|
}
|
|
|
|
|
2018-08-01 19:25:58 +02:00
|
|
|
/* This one needs to work on a local tree. */
|
2019-04-17 06:17:24 +02:00
|
|
|
void ntreeGPUMaterialNodes(bNodeTree *localtree,
|
|
|
|
GPUMaterial *mat,
|
|
|
|
bool *has_surface_output,
|
|
|
|
bool *has_volume_output)
|
2017-10-27 16:07:44 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNodeTreeExec *exec;
|
2018-07-05 17:20:44 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* Extract output nodes from inside nodegroups. */
|
|
|
|
ntree_shader_output_node_from_group(localtree, SHD_OUTPUT_EEVEE);
|
2019-03-05 17:24:31 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
bNode *output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE);
|
2019-03-05 17:24:31 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
ntree_shader_groups_expand_inputs(localtree);
|
2018-11-01 15:06:23 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* Perform all needed modifications on the tree in order to support
|
|
|
|
* displacement/bump mapping.
|
|
|
|
*/
|
|
|
|
ntree_shader_relink_displacement(localtree, output);
|
2018-07-05 17:20:44 +02:00
|
|
|
|
2019-08-19 20:33:17 +02:00
|
|
|
/* Duplicate bump height branches for manual derivatives.
|
|
|
|
*/
|
|
|
|
nodeChainIter(localtree, output, ntree_shader_bump_branches, localtree, true);
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* TODO(fclem): consider moving this to the gpu shader tree evaluation. */
|
|
|
|
nTreeTags tags = {
|
|
|
|
.ssr_id = 1.0,
|
|
|
|
.sss_id = 1.0,
|
|
|
|
};
|
|
|
|
ntree_shader_tag_nodes(localtree, output, &tags);
|
2017-10-27 16:07:44 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
exec = ntreeShaderBeginExecTree(localtree);
|
|
|
|
ntreeExecGPUNodes(exec, mat, output);
|
|
|
|
ntreeShaderEndExecTree(exec);
|
2017-10-27 16:07:44 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* EEVEE: Find which material domain was used (volume, surface ...). */
|
|
|
|
*has_surface_output = false;
|
|
|
|
*has_volume_output = false;
|
2017-10-27 16:07:44 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (output != NULL) {
|
|
|
|
bNodeSocket *surface_sock = ntree_shader_node_find_input(output, "Surface");
|
|
|
|
bNodeSocket *volume_sock = ntree_shader_node_find_input(output, "Volume");
|
2017-10-27 16:07:44 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (surface_sock != NULL) {
|
|
|
|
*has_surface_output = (nodeCountSocketLinks(localtree, surface_sock) > 0);
|
|
|
|
}
|
2017-10-27 16:07:44 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (volume_sock != NULL) {
|
|
|
|
*has_volume_output = (nodeCountSocketLinks(localtree, volume_sock) > 0);
|
|
|
|
}
|
|
|
|
}
|
2017-10-27 16:07:44 +02:00
|
|
|
}
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
bNodeTreeExec *ntreeShaderBeginExecTree_internal(bNodeExecContext *context,
|
|
|
|
bNodeTree *ntree,
|
|
|
|
bNodeInstanceKey parent_key)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNodeTreeExec *exec;
|
|
|
|
bNode *node;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* ensures only a single output node is enabled */
|
|
|
|
ntreeSetOutput(ntree);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* common base initialization */
|
|
|
|
exec = ntree_exec_begin(context, ntree, parent_key);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* allocate the thread stack listbase array */
|
|
|
|
exec->threadstack = MEM_callocN(BLENDER_MAX_THREADS * sizeof(ListBase), "thread stack array");
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-22 13:31:31 +10:00
|
|
|
for (node = exec->nodetree->nodes.first; node; node = node->next) {
|
2019-04-17 06:17:24 +02:00
|
|
|
node->need_exec = 1;
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
return exec;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bNodeTreeExec *ntreeShaderBeginExecTree(bNodeTree *ntree)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNodeExecContext context;
|
|
|
|
bNodeTreeExec *exec;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* XXX hack: prevent exec data from being generated twice.
|
|
|
|
* this should be handled by the renderer!
|
|
|
|
*/
|
2019-04-22 13:31:31 +10:00
|
|
|
if (ntree->execdata) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return ntree->execdata;
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
context.previews = ntree->previews;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
exec = ntreeShaderBeginExecTree_internal(&context, ntree, NODE_INSTANCE_KEY_BASE);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* XXX this should not be necessary, but is still used for cmp/sha/tex nodes,
|
|
|
|
* which only store the ntree pointer. Should be fixed at some point!
|
|
|
|
*/
|
|
|
|
ntree->execdata = exec;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
return exec;
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
void ntreeShaderEndExecTree_internal(bNodeTreeExec *exec)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bNodeThreadStack *nts;
|
|
|
|
int a;
|
|
|
|
|
|
|
|
if (exec->threadstack) {
|
|
|
|
for (a = 0; a < BLENDER_MAX_THREADS; a++) {
|
2019-04-22 13:31:31 +10:00
|
|
|
for (nts = exec->threadstack[a].first; nts; nts = nts->next) {
|
|
|
|
if (nts->stack) {
|
2019-04-17 06:17:24 +02:00
|
|
|
MEM_freeN(nts->stack);
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_freelistN(&exec->threadstack[a]);
|
|
|
|
}
|
|
|
|
|
|
|
|
MEM_freeN(exec->threadstack);
|
|
|
|
exec->threadstack = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ntree_exec_end(exec);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ntreeShaderEndExecTree(bNodeTreeExec *exec)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
if (exec) {
|
|
|
|
/* exec may get freed, so assign ntree */
|
|
|
|
bNodeTree *ntree = exec->nodetree;
|
|
|
|
ntreeShaderEndExecTree_internal(exec);
|
|
|
|
|
|
|
|
/* XXX clear nodetree backpointer to exec data, same problem as noted in ntreeBeginExecTree */
|
|
|
|
ntree->execdata = NULL;
|
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
|
|
|
|
Remove Blender Internal and legacy viewport from Blender 2.8.
Brecht authored this commit, but he gave me the honours to actually
do it. Here it goes; Blender Internal. Bye bye, you did great!
* Point density, voxel data, ocean, environment map textures were removed,
as these only worked within BI rendering. Note that the ocean modifier
and the Cycles point density shader node continue to work.
* Dynamic paint using material shading was removed, as this only worked
with BI. If we ever wanted to support this again probably it should go
through the baking API.
* GPU shader export through the Python API was removed. This only worked
for the old BI GLSL shaders, which no longer exists. Doing something
similar for Eevee would be significantly more complicated because it
uses a lot of multiplass rendering and logic outside the shader, it's
probably impractical.
* Collada material import / export code is mostly gone, as it only worked
for BI materials. We need to add Cycles / Eevee material support at some
point.
* The mesh noise operator was removed since it only worked with BI
material texture slots. A displacement modifier can be used instead.
* The delete texture paint slot operator was removed since it only worked
for BI material texture slots. Could be added back with node support.
* Not all legacy viewport features are supported in the new viewport, but
their code was removed. If we need to bring anything back we can look at
older git revisions.
* There is some legacy viewport code that I could not remove yet, and some
that I probably missed.
* Shader node execution code was left mostly intact, even though it is not
used anywhere now. We may eventually use this to replace the texture
nodes with Cycles / Eevee shader nodes.
* The Cycles Bake panel now includes settings for baking multires normal
and displacement maps. The underlying code needs to be merged properly,
and we plan to add back support for multires AO baking and add support
to Cycles baking for features like vertex color, displacement, and other
missing baking features.
* This commit removes DNA and the Python API for BI material, lamp, world
and scene settings. This breaks a lot of addons.
* There is more DNA that can be removed or renamed, where Cycles or Eevee
are reusing some old BI properties but the names are not really correct
anymore.
* Texture slots for materials, lamps and world were removed. They remain
for brushes, particles and freestyle linestyles.
* 'BLENDER_RENDER' remains in the COMPAT_ENGINES of UI panels. Cycles and
other renderers use this to find all panels to show, minus a few panels
that they have their own replacement for.
2018-04-19 17:34:44 +02:00
|
|
|
/* TODO: left over from Blender Internal, could reuse for new texture nodes. */
|
|
|
|
bool ntreeShaderExecTree(bNodeTree *ntree, int thread)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
ShaderCallData scd;
|
|
|
|
bNodeThreadStack *nts = NULL;
|
|
|
|
bNodeTreeExec *exec = ntree->execdata;
|
|
|
|
int compat;
|
|
|
|
|
|
|
|
/* ensure execdata is only initialized once */
|
|
|
|
if (!exec) {
|
|
|
|
BLI_thread_lock(LOCK_NODES);
|
2019-04-22 13:31:31 +10:00
|
|
|
if (!ntree->execdata) {
|
2019-04-17 06:17:24 +02:00
|
|
|
ntree->execdata = ntreeShaderBeginExecTree(ntree);
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_thread_unlock(LOCK_NODES);
|
|
|
|
|
|
|
|
exec = ntree->execdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
nts = ntreeGetThreadStack(exec, thread);
|
|
|
|
compat = ntreeExecThreadNodes(exec, nts, &scd, thread);
|
|
|
|
ntreeReleaseThreadStack(nts);
|
|
|
|
|
|
|
|
/* if compat is zero, it has been using non-compatible nodes */
|
|
|
|
return compat;
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|