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"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "DNA_linestyle_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"
|
2017-10-16 17:15:03 -02:00
|
|
|
#include "DNA_workspace_types.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "DNA_world_types.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_alloca.h"
|
2019-09-23 11:34:22 +02:00
|
|
|
#include "BLI_linklist.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "BLI_listbase.h"
|
|
|
|
#include "BLI_threads.h"
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
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"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BKE_lib_id.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"
|
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"
|
|
|
|
|
2020-11-09 15:42:38 +01:00
|
|
|
#include "RE_texture.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
|
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_shader_util.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "node_util.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2018-12-03 17:19:04 +01:00
|
|
|
typedef struct nTreeTags {
|
|
|
|
float ssr_id, sss_id;
|
|
|
|
} 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
|
|
|
{
|
2013-03-18 16:34:57 +00:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2018-04-17 13:35:05 +02:00
|
|
|
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. */
|
2017-10-16 17:15:03 -02:00
|
|
|
return (engine_id[0] == '\0' || STREQ(engine_id, RE_engine_id_CYCLES) ||
|
2018-04-17 13:35:05 +02:00
|
|
|
!BKE_scene_use_shading_nodes_custom(scene));
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2011-11-02 18:55:32 +00:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
static void shader_get_from_context(const bContext *C,
|
|
|
|
bNodeTreeType *UNUSED(treetype),
|
|
|
|
bNodeTree **r_ntree,
|
|
|
|
ID **r_id,
|
|
|
|
ID **r_from)
|
|
|
|
{
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
2017-11-22 10:52:39 -02:00
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
|
|
Object *ob = OBACT(view_layer);
|
2019-04-17 06:17:24 +02: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
|
|
|
if (snode->shaderfrom == SNODE_SHADER_OBJECT) {
|
2013-03-18 16:34:57 +00:00
|
|
|
if (ob) {
|
2013-03-19 10:42:33 +00:00
|
|
|
*r_from = &ob->id;
|
2013-03-18 16:34:57 +00:00
|
|
|
if (ob->type == OB_LAMP) {
|
|
|
|
*r_id = ob->data;
|
2019-02-27 10:46:48 +11:00
|
|
|
*r_ntree = ((Light *)ob->data)->nodetree;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-02-05 11:23:58 +01:00
|
|
|
Material *ma = BKE_object_material_get(ob, ob->actcol);
|
2013-03-18 16:34:57 +00:00
|
|
|
if (ma) {
|
|
|
|
*r_id = &ma->id;
|
|
|
|
*r_ntree = ma->nodetree;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-11 16:51:04 +09:00
|
|
|
#ifdef WITH_FREESTYLE
|
|
|
|
else if (snode->shaderfrom == SNODE_SHADER_LINESTYLE) {
|
2018-04-24 15:20:17 +02:00
|
|
|
FreestyleLineStyle *linestyle = BKE_linestyle_active_from_view_layer(view_layer);
|
2014-07-11 16:51:04 +09:00
|
|
|
if (linestyle) {
|
|
|
|
*r_from = NULL;
|
|
|
|
*r_id = &linestyle->id;
|
|
|
|
*r_ntree = linestyle->nodetree;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2013-03-18 16:34:57 +00: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
|
|
|
{
|
2012-05-16 15:01:46 +00:00
|
|
|
func(calldata, NODE_CLASS_INPUT, N_("Input"));
|
|
|
|
func(calldata, NODE_CLASS_OUTPUT, N_("Output"));
|
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
|
|
|
func(calldata, NODE_CLASS_SHADER, N_("Shader"));
|
|
|
|
func(calldata, NODE_CLASS_TEXTURE, N_("Texture"));
|
2012-05-16 15:01:46 +00:00
|
|
|
func(calldata, NODE_CLASS_OP_COLOR, N_("Color"));
|
|
|
|
func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector"));
|
|
|
|
func(calldata, NODE_CLASS_CONVERTOR, N_("Convertor"));
|
2012-11-03 14:32:26 +00:00
|
|
|
func(calldata, NODE_CLASS_SCRIPT, N_("Script"));
|
2012-05-16 15:01:46 +00:00
|
|
|
func(calldata, NODE_CLASS_GROUP, N_("Group"));
|
2013-03-18 16:34:57 +00:00
|
|
|
func(calldata, NODE_CLASS_INTERFACE, N_("Interface"));
|
2012-05-16 15:01:46 +00:00
|
|
|
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))
|
|
|
|
{
|
|
|
|
bNode *node, *node_next;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2012-06-01 12:38:03 +00:00
|
|
|
/* replace muted nodes and reroute nodes by internal links */
|
2012-09-03 02:41:12 +00:00
|
|
|
for (node = localtree->nodes.first; node; node = node_next) {
|
2012-02-27 17:38:16 +00:00
|
|
|
node_next = node->next;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2012-06-01 12:38:03 +00:00
|
|
|
if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) {
|
2012-02-27 17:38:16 +00:00
|
|
|
nodeInternalRelink(localtree, node);
|
2019-03-16 18:54:00 +01:00
|
|
|
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
|
|
|
{
|
2013-03-22 10:34:52 +00:00
|
|
|
BKE_node_preview_sync_tree(ntree, localtree);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
ntreeSetOutput(ntree);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2012-08-06 18:49:28 +00:00
|
|
|
ntree_update_reroute_nodes(ntree);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2013-03-18 16:34:57 +00: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;
|
|
|
|
}
|
|
|
|
|
2021-04-29 23:36:46 -05:00
|
|
|
static bool shader_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
|
|
|
|
bNodeTreeType *UNUSED(ntreetype))
|
|
|
|
{
|
|
|
|
return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
bNodeTreeType *tt = ntreeType_Shader = MEM_callocN(sizeof(bNodeTreeType),
|
|
|
|
"shader node tree type");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
tt->type = NTREE_SHADER;
|
|
|
|
strcpy(tt->idname, "ShaderNodeTree");
|
2019-02-13 16:18:19 +01:00
|
|
|
strcpy(tt->ui_name, N_("Shader Editor"));
|
2013-05-27 08:04:07 +00:00
|
|
|
tt->ui_icon = 0; /* defined in drawnode.c */
|
2019-02-13 16:18:19 +01:00
|
|
|
strcpy(tt->ui_description, N_("Shader nodes"));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
tt->foreach_nodeclass = foreach_nodeclass;
|
|
|
|
tt->localize = localize;
|
|
|
|
tt->local_sync = local_sync;
|
2013-03-22 10:34:52 +00:00
|
|
|
tt->local_merge = local_merge;
|
2013-03-18 16:34:57 +00:00
|
|
|
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;
|
2021-04-29 23:36:46 -05:00
|
|
|
tt->valid_socket_type = shader_node_tree_socket_type_valid;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-03 18:24:08 +02:00
|
|
|
tt->rna_ext.srna = &RNA_ShaderNodeTree;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
ntreeTypeAdd(tt);
|
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
|
|
|
|
/* GPU material from shader nodes */
|
|
|
|
|
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
|
|
|
{
|
|
|
|
/* Make sure we only have single node tagged as output. */
|
|
|
|
ntreeSetOutput(ntree);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-05 12:44:15 +02:00
|
|
|
/* Find output node that matches type and target. If there are
|
|
|
|
* multiple, we prefer exact target match and active nodes. */
|
|
|
|
bNode *output_node = NULL;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
2018-07-05 12:44:15 +02:00
|
|
|
if (!ELEM(node->type, SH_NODE_OUTPUT_MATERIAL, SH_NODE_OUTPUT_WORLD, SH_NODE_OUTPUT_LIGHT)) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-05 12:44:15 +02:00
|
|
|
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;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2018-07-05 12:44:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (node->custom1 == target) {
|
|
|
|
if (output_node == NULL) {
|
|
|
|
output_node = node;
|
|
|
|
}
|
2018-07-10 09:29:30 +02:00
|
|
|
else if (output_node->custom1 == SHD_OUTPUT_ALL) {
|
2018-07-05 12:44:15 +02:00
|
|
|
output_node = node;
|
|
|
|
}
|
|
|
|
else if ((node->flag & NODE_DO_OUTPUT) && !(output_node->flag & NODE_DO_OUTPUT)) {
|
|
|
|
output_node = node;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2018-07-05 12:44:15 +02:00
|
|
|
}
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-07-05 12:44:15 +02:00
|
|
|
return output_node;
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Find socket with a specified identifier. */
|
|
|
|
static bNodeSocket *ntree_shader_node_find_socket(ListBase *sockets, const char *identifier)
|
|
|
|
{
|
|
|
|
for (bNodeSocket *sock = sockets->first; sock != NULL; sock = sock->next) {
|
|
|
|
if (STREQ(sock->identifier, identifier)) {
|
|
|
|
return sock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find input socket with a specified identifier. */
|
|
|
|
static bNodeSocket *ntree_shader_node_find_input(bNode *node, const char *identifier)
|
|
|
|
{
|
|
|
|
return ntree_shader_node_find_socket(&node->inputs, identifier);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find output socket with a specified identifier. */
|
|
|
|
static bNodeSocket *ntree_shader_node_find_output(bNode *node, const char *identifier)
|
|
|
|
{
|
|
|
|
return ntree_shader_node_find_socket(&node->outputs, identifier);
|
|
|
|
}
|
|
|
|
|
2020-03-23 23:50:24 +01:00
|
|
|
/* Return true on success. */
|
|
|
|
static bool ntree_shader_expand_socket_default(bNodeTree *localtree,
|
|
|
|
bNode *node,
|
|
|
|
bNodeSocket *socket)
|
|
|
|
{
|
|
|
|
bNode *value_node;
|
|
|
|
bNodeSocket *value_socket;
|
|
|
|
bNodeSocketValueVector *src_vector;
|
|
|
|
bNodeSocketValueRGBA *src_rgba, *dst_rgba;
|
|
|
|
bNodeSocketValueFloat *src_float, *dst_float;
|
|
|
|
bNodeSocketValueInt *src_int;
|
|
|
|
|
|
|
|
switch (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 = 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 = 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 = 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 = socket->default_value;
|
|
|
|
dst_float = value_socket->default_value;
|
|
|
|
dst_float->value = src_float->value;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
nodeAddLink(localtree, value_node, value_socket, node, socket);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-01 15:06:23 +01:00
|
|
|
static void ntree_shader_unlink_hidden_value_sockets(bNode *group_node, bNodeSocket *isock)
|
|
|
|
{
|
|
|
|
bNodeTree *group_ntree = (bNodeTree *)group_node->id;
|
|
|
|
bNode *node;
|
|
|
|
bool removed_link = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-01 15:06:23 +01:00
|
|
|
for (node = group_ntree->nodes.first; node; node = node->next) {
|
2020-08-01 19:56:53 +03:00
|
|
|
const bool is_group = ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && (node->id != NULL);
|
|
|
|
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
2020-08-01 19:56:53 +03:00
|
|
|
if (!is_group && (sock->flag & SOCK_HIDE_VALUE) == 0) {
|
2018-11-01 15:06:23 +01:00
|
|
|
continue;
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2018-11-01 15:06:23 +01: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)) {
|
2020-08-01 19:56:53 +03:00
|
|
|
if (is_group) {
|
|
|
|
/* Recursively unlink sockets within the nested group. */
|
|
|
|
ntree_shader_unlink_hidden_value_sockets(node, sock);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nodeRemLink(group_ntree, sock->link);
|
|
|
|
removed_link = true;
|
|
|
|
}
|
2018-11-01 15:06:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-01 15:06:23 +01:00
|
|
|
if (removed_link) {
|
|
|
|
ntreeUpdateTree(G.main, group_ntree);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
bool link_added = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-23 23:50:24 +01:00
|
|
|
LISTBASE_FOREACH (bNode *, node, &localtree->nodes) {
|
|
|
|
const bool is_group = ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && (node->id != NULL);
|
|
|
|
const bool is_group_output = node->type == NODE_GROUP_OUTPUT && (node->flag & NODE_DO_OUTPUT);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-23 23:50:24 +01:00
|
|
|
if (is_group) {
|
|
|
|
/* Do it recursively. */
|
|
|
|
ntree_shader_groups_expand_inputs((bNodeTree *)node->id);
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-23 23:50:24 +01:00
|
|
|
if (is_group || is_group_output) {
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
|
2021-03-16 19:11:54 +00:00
|
|
|
if (socket->link != NULL && !(socket->link->flag & NODE_LINK_MUTED)) {
|
2020-03-23 23:50:24 +01:00
|
|
|
bNodeLink *link = socket->link;
|
|
|
|
/* Fix the case where the socket is actually converting the data. (see T71374)
|
|
|
|
* We only do the case of lossy conversion to float.*/
|
|
|
|
if ((socket->type == SOCK_FLOAT) && (link->fromsock->type != link->tosock->type)) {
|
2020-05-14 19:56:14 +02:00
|
|
|
if (link->fromsock->type == SOCK_RGBA) {
|
|
|
|
bNode *tmp = nodeAddStaticNode(NULL, localtree, SH_NODE_RGBTOBW);
|
|
|
|
nodeAddLink(localtree, link->fromnode, link->fromsock, tmp, tmp->inputs.first);
|
|
|
|
nodeAddLink(localtree, tmp, tmp->outputs.first, node, socket);
|
|
|
|
}
|
|
|
|
else if (link->fromsock->type == SOCK_VECTOR) {
|
|
|
|
bNode *tmp = nodeAddStaticNode(NULL, localtree, SH_NODE_VECTOR_MATH);
|
|
|
|
tmp->custom1 = NODE_VECTOR_MATH_DOT_PRODUCT;
|
|
|
|
bNodeSocket *dot_input1 = tmp->inputs.first;
|
|
|
|
bNodeSocket *dot_input2 = dot_input1->next;
|
|
|
|
bNodeSocketValueVector *input2_socket_value = dot_input2->default_value;
|
|
|
|
copy_v3_fl(input2_socket_value->value, 1.0f / 3.0f);
|
|
|
|
nodeAddLink(localtree, link->fromnode, link->fromsock, tmp, dot_input1);
|
|
|
|
nodeAddLink(localtree, tmp, tmp->outputs.last, node, socket);
|
|
|
|
}
|
2020-03-23 23:50:24 +01:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2020-01-23 17:40:40 +01:00
|
|
|
|
2020-03-23 23:50:24 +01:00
|
|
|
if (is_group) {
|
|
|
|
/* 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(node, socket);
|
2020-01-23 17:40:40 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-23 23:50:24 +01:00
|
|
|
if (ntree_shader_expand_socket_default(localtree, node, socket)) {
|
|
|
|
link_added = true;
|
|
|
|
}
|
2018-11-01 15:06:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-01 15:06:23 +01:00
|
|
|
if (link_added) {
|
|
|
|
ntreeUpdateTree(G.main, localtree);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 11:34:22 +02:00
|
|
|
static void flatten_group_do(bNodeTree *ntree, bNode *gnode)
|
|
|
|
{
|
|
|
|
bNodeLink *link, *linkn, *tlink;
|
|
|
|
bNode *node, *nextnode;
|
|
|
|
bNodeTree *ngroup;
|
|
|
|
LinkNode *group_interface_nodes = NULL;
|
|
|
|
|
|
|
|
ngroup = (bNodeTree *)gnode->id;
|
|
|
|
|
|
|
|
/* Add the nodes into the ntree */
|
|
|
|
for (node = ngroup->nodes.first; node; node = nextnode) {
|
|
|
|
nextnode = node->next;
|
|
|
|
/* Remove interface nodes.
|
|
|
|
* This also removes remaining links to and from interface nodes.
|
|
|
|
* We must delay removal since sockets will reference this node. see: T52092 */
|
|
|
|
if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {
|
|
|
|
BLI_linklist_prepend(&group_interface_nodes, node);
|
|
|
|
}
|
|
|
|
/* migrate node */
|
|
|
|
BLI_remlink(&ngroup->nodes, node);
|
|
|
|
BLI_addtail(&ntree->nodes, node);
|
|
|
|
/* ensure unique node name in the node tree */
|
2019-10-09 23:45:31 +02:00
|
|
|
/* This is very slow and it has no use for GPU nodetree. (see T70609) */
|
|
|
|
// nodeUniqueName(ntree, node);
|
2019-09-23 11:34:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Save first and last link to iterate over flattened group links. */
|
|
|
|
bNodeLink *glinks_first = ntree->links.last;
|
|
|
|
|
|
|
|
/* Add internal links to the ntree */
|
|
|
|
for (link = ngroup->links.first; link; link = linkn) {
|
|
|
|
linkn = link->next;
|
|
|
|
BLI_remlink(&ngroup->links, link);
|
|
|
|
BLI_addtail(&ntree->links, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
bNodeLink *glinks_last = ntree->links.last;
|
|
|
|
|
|
|
|
/* restore external links to and from the gnode */
|
|
|
|
if (glinks_first != NULL) {
|
|
|
|
/* input links */
|
|
|
|
for (link = glinks_first->next; link != glinks_last->next; link = link->next) {
|
|
|
|
if (link->fromnode->type == NODE_GROUP_INPUT) {
|
|
|
|
const char *identifier = link->fromsock->identifier;
|
|
|
|
/* find external links to this input */
|
|
|
|
for (tlink = ntree->links.first; tlink != glinks_first->next; tlink = tlink->next) {
|
|
|
|
if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) {
|
|
|
|
nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-10-02 15:47:36 +02:00
|
|
|
/* Also iterate over the new links to cover passthrough links. */
|
|
|
|
glinks_last = ntree->links.last;
|
2019-09-23 11:34:22 +02:00
|
|
|
/* output links */
|
|
|
|
for (tlink = ntree->links.first; tlink != glinks_first->next; tlink = tlink->next) {
|
|
|
|
if (tlink->fromnode == gnode) {
|
|
|
|
const char *identifier = tlink->fromsock->identifier;
|
|
|
|
/* find internal links to this output */
|
|
|
|
for (link = glinks_first->next; link != glinks_last->next; link = link->next) {
|
|
|
|
/* only use active output node */
|
|
|
|
if (link->tonode->type == NODE_GROUP_OUTPUT && (link->tonode->flag & NODE_DO_OUTPUT)) {
|
|
|
|
if (STREQ(link->tosock->identifier, identifier)) {
|
|
|
|
nodeAddLink(ntree, link->fromnode, link->fromsock, tlink->tonode, tlink->tosock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (group_interface_nodes) {
|
|
|
|
node = BLI_linklist_pop(&group_interface_nodes);
|
|
|
|
ntreeFreeLocalNode(ntree, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Flatten group to only have a simple single tree */
|
|
|
|
static void ntree_shader_groups_flatten(bNodeTree *localtree)
|
|
|
|
{
|
2019-09-30 17:06:28 +10:00
|
|
|
/* This is effectively recursive as the flattened groups will add
|
2019-09-23 11:34:22 +02:00
|
|
|
* nodes at the end of the list, which will also get evaluated. */
|
|
|
|
for (bNode *node = localtree->nodes.first, *node_next; node; node = node_next) {
|
|
|
|
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id != NULL) {
|
|
|
|
flatten_group_do(localtree, node);
|
|
|
|
/* Continue even on new flattened nodes. */
|
|
|
|
node_next = node->next;
|
|
|
|
/* delete the group instance and its localtree. */
|
|
|
|
bNodeTree *ngroup = (bNodeTree *)node->id;
|
|
|
|
ntreeFreeLocalNode(localtree, node);
|
|
|
|
ntreeFreeTree(ngroup);
|
|
|
|
MEM_freeN(ngroup);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
node_next = node->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ntreeUpdateTree(G.main, localtree);
|
|
|
|
}
|
|
|
|
|
2016-05-20 14:16:54 +02:00
|
|
|
/* Check whether shader has a displacement.
|
|
|
|
*
|
|
|
|
* Will also return a node and its 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)
|
|
|
|
{
|
|
|
|
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");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-20 14:16:54 +02:00
|
|
|
if (displacement == NULL) {
|
|
|
|
/* Non-cycles node is used as an output. */
|
|
|
|
return false;
|
|
|
|
}
|
2021-05-14 15:42:07 +02:00
|
|
|
|
2021-03-16 19:11:54 +00:00
|
|
|
if ((displacement->link != NULL) && !(displacement->link->flag & NODE_LINK_MUTED)) {
|
2016-05-20 14:16:54 +02:00
|
|
|
*r_node = displacement->link->fromnode;
|
|
|
|
*r_socket = displacement->link->fromsock;
|
|
|
|
*r_link = displacement->link;
|
2021-05-14 15:42:07 +02:00
|
|
|
return true;
|
2016-05-20 14:16:54 +02:00
|
|
|
}
|
2021-05-14 15:42:07 +02:00
|
|
|
return false;
|
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)
|
|
|
|
{
|
|
|
|
/* TODO(sergey): Can we do something smarter here than just a name-based
|
|
|
|
* matching?
|
|
|
|
*/
|
2020-04-03 19:15:01 +02:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
2019-06-26 12:03:15 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
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,
|
2019-09-23 11:34:22 +02:00
|
|
|
bNodeSocket *socket_from)
|
2016-05-20 14:16:54 +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;
|
|
|
|
}
|
2016-06-14 11:31:00 +02:00
|
|
|
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 */
|
2019-09-23 11:34:22 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
2019-06-26 12:03:15 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
|
|
|
}
|
|
|
|
|
2019-09-30 13:09:39 +02:00
|
|
|
static bool ntree_branch_count_and_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata)
|
2019-06-26 12:03:15 +02:00
|
|
|
{
|
|
|
|
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-10-10 00:31:47 +02:00
|
|
|
nodeChainIterBackwards(ntree, start_node, ntree_branch_count_and_tag_nodes, &node_count, 1);
|
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;
|
2019-12-07 19:35:52 +01:00
|
|
|
nodes_copy[id] = BKE_node_copy_ex(
|
2020-01-16 15:04:06 +01:00
|
|
|
ntree, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN, false);
|
2019-06-26 12:03:15 +02:00
|
|
|
nodes_copy[id]->tmp_flag = -2; /* Copy */
|
|
|
|
/* 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
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2016-05-20 14:16:54 +02:00
|
|
|
/* We have to disconnect displacement output socket, otherwise we'll have
|
|
|
|
* cycles in the Cycles material :)
|
|
|
|
*/
|
|
|
|
nodeRemLink(ntree, displacement_link);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-01-20 02:01:07 +01:00
|
|
|
/* 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);
|
2018-01-20 02:01:07 +01: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);
|
2018-01-20 02:01:07 +01:00
|
|
|
displacement_node = dot_node;
|
|
|
|
displacement_socket = ntree_shader_node_find_output(dot_node, "Value");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-05-20 14:16:54 +02:00
|
|
|
/* 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-09-23 11:34:22 +02:00
|
|
|
ntree_shader_link_builtin_normal(ntree, bump_node, bump_output_socket);
|
2016-05-20 14:16:54 +02:00
|
|
|
/* We modified the tree, it needs to be updated now. */
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-10 00:31:47 +02:00
|
|
|
static bool ntree_shader_bump_branches(bNode *fromnode, bNode *UNUSED(tonode), void *userdata)
|
2019-08-19 20:33:17 +02:00
|
|
|
{
|
|
|
|
bNodeTree *ntree = (bNodeTree *)userdata;
|
|
|
|
|
2019-10-10 00:31:47 +02:00
|
|
|
if (fromnode->type == SH_NODE_BUMP) {
|
2019-08-19 20:33:17 +02:00
|
|
|
bNodeSocket *height_dx_sock, *height_dy_sock, *bump_socket, *bump_dx_socket, *bump_dy_socket;
|
2019-10-10 00:31:47 +02:00
|
|
|
bNode *bump = fromnode;
|
2019-08-19 20:33:17 +02:00
|
|
|
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-09-30 13:09:39 +02:00
|
|
|
static bool ntree_tag_bsdf_cb(bNode *fromnode, bNode *UNUSED(tonode), void *userdata)
|
2017-07-15 16:09:44 +02:00
|
|
|
{
|
|
|
|
switch (fromnode->type) {
|
|
|
|
case SH_NODE_BSDF_ANISOTROPIC:
|
|
|
|
case SH_NODE_EEVEE_SPECULAR:
|
|
|
|
case SH_NODE_BSDF_GLOSSY:
|
2017-08-15 09:36:23 +02:00
|
|
|
case SH_NODE_BSDF_GLASS:
|
2018-12-03 17:19:04 +01:00
|
|
|
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;
|
2017-07-15 16:09:44 +02:00
|
|
|
break;
|
|
|
|
default:
|
2018-12-03 17:19:04 +01:00
|
|
|
/* We could return false here but since we
|
2017-07-15 16:09:44 +02:00
|
|
|
* allow the use of Closure as RGBA, we can have
|
|
|
|
* Bsdf nodes linked to other Bsdf nodes. */
|
|
|
|
break;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-07-15 16:09:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|
|
|
{
|
|
|
|
if (output_node == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Make sure sockets links pointers are correct. */
|
|
|
|
ntreeUpdateTree(G.main, ntree);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-10-10 00:31:47 +02:00
|
|
|
nodeChainIterBackwards(ntree, output_node, ntree_tag_bsdf_cb, tags, 0);
|
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. */
|
|
|
|
void ntreeGPUMaterialNodes(bNodeTree *localtree,
|
|
|
|
GPUMaterial *mat,
|
|
|
|
bool *has_surface_output,
|
|
|
|
bool *has_volume_output)
|
2017-10-27 16:07:44 +02:00
|
|
|
{
|
2018-07-05 17:20:44 +02:00
|
|
|
bNodeTreeExec *exec;
|
|
|
|
|
2019-03-05 17:24:31 +01:00
|
|
|
bNode *output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE);
|
|
|
|
|
2018-11-01 15:06:23 +01:00
|
|
|
ntree_shader_groups_expand_inputs(localtree);
|
|
|
|
|
2019-09-23 11:34:22 +02:00
|
|
|
ntree_shader_groups_flatten(localtree);
|
|
|
|
|
|
|
|
if (output == NULL) {
|
|
|
|
/* Search again, now including flattened nodes. */
|
|
|
|
output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE);
|
|
|
|
}
|
|
|
|
|
2018-07-05 17:20:44 +02:00
|
|
|
/* Perform all needed modifications on the tree in order to support
|
|
|
|
* displacement/bump mapping.
|
|
|
|
*/
|
|
|
|
ntree_shader_relink_displacement(localtree, output);
|
|
|
|
|
2019-08-19 20:33:17 +02:00
|
|
|
/* Duplicate bump height branches for manual derivatives.
|
|
|
|
*/
|
2019-10-10 00:31:47 +02:00
|
|
|
nodeChainIterBackwards(localtree, output, ntree_shader_bump_branches, localtree, 0);
|
2020-12-04 08:13:54 +01:00
|
|
|
LISTBASE_FOREACH (bNode *, node, &localtree->nodes) {
|
|
|
|
if (node->type == SH_NODE_OUTPUT_AOV) {
|
|
|
|
nodeChainIterBackwards(localtree, node, ntree_shader_bump_branches, localtree, 0);
|
|
|
|
nTreeTags tags = {
|
|
|
|
.ssr_id = 1.0,
|
|
|
|
.sss_id = 1.0,
|
|
|
|
};
|
|
|
|
ntree_shader_tag_nodes(localtree, node, &tags);
|
|
|
|
}
|
|
|
|
}
|
2019-08-19 20:33:17 +02:00
|
|
|
|
2018-12-03 17:19:04 +01:00
|
|
|
/* TODO(fclem): consider moving this to the gpu shader tree evaluation. */
|
|
|
|
nTreeTags tags = {
|
|
|
|
.ssr_id = 1.0,
|
2019-01-07 00:06:58 +11:00
|
|
|
.sss_id = 1.0,
|
2018-12-03 17:19:04 +01:00
|
|
|
};
|
|
|
|
ntree_shader_tag_nodes(localtree, output, &tags);
|
2017-10-27 16:07:44 +02:00
|
|
|
|
2018-07-05 17:20:44 +02:00
|
|
|
exec = ntreeShaderBeginExecTree(localtree);
|
2019-04-02 16:37:54 +02:00
|
|
|
ntreeExecGPUNodes(exec, mat, output);
|
2020-12-04 08:13:54 +01:00
|
|
|
LISTBASE_FOREACH (bNode *, node, &localtree->nodes) {
|
|
|
|
if (node->type == SH_NODE_OUTPUT_AOV) {
|
|
|
|
ntreeExecGPUNodes(exec, mat, node);
|
|
|
|
}
|
|
|
|
}
|
2018-07-05 17:20:44 +02:00
|
|
|
ntreeShaderEndExecTree(exec);
|
2017-10-27 16:07:44 +02:00
|
|
|
|
2018-07-05 17:20:44 +02:00
|
|
|
/* EEVEE: Find which material domain was used (volume, surface ...). */
|
2017-10-27 16:07:44 +02:00
|
|
|
*has_surface_output = false;
|
|
|
|
*has_volume_output = false;
|
|
|
|
|
|
|
|
if (output != NULL) {
|
|
|
|
bNodeSocket *surface_sock = ntree_shader_node_find_input(output, "Surface");
|
|
|
|
bNodeSocket *volume_sock = ntree_shader_node_find_input(output, "Volume");
|
|
|
|
|
|
|
|
if (surface_sock != NULL) {
|
|
|
|
*has_surface_output = (nodeCountSocketLinks(localtree, surface_sock) > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (volume_sock != NULL) {
|
|
|
|
*has_volume_output = (nodeCountSocketLinks(localtree, volume_sock) > 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
bNodeTreeExec *ntreeShaderBeginExecTree_internal(bNodeExecContext *context,
|
|
|
|
bNodeTree *ntree,
|
|
|
|
bNodeInstanceKey parent_key)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
|
|
|
bNodeTreeExec *exec;
|
|
|
|
bNode *node;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2011-09-05 21:01:50 +00:00
|
|
|
/* ensures only a single output node is enabled */
|
|
|
|
ntreeSetOutput(ntree);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2011-09-05 21:01:50 +00:00
|
|
|
/* common base initialization */
|
2013-03-18 16:34:57 +00:00
|
|
|
exec = ntree_exec_begin(context, ntree, parent_key);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2011-09-05 21:01:50 +00:00
|
|
|
/* allocate the thread stack listbase array */
|
2015-06-08 13:46:33 +02:00
|
|
|
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) {
|
2012-09-03 02:41:12 +00:00
|
|
|
node->need_exec = 1;
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
return exec;
|
|
|
|
}
|
|
|
|
|
|
|
|
bNodeTreeExec *ntreeShaderBeginExecTree(bNodeTree *ntree)
|
|
|
|
{
|
|
|
|
bNodeExecContext context;
|
|
|
|
bNodeTreeExec *exec;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2013-03-18 16:34:57 +00: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) {
|
2013-03-18 16:34:57 +00:00
|
|
|
return ntree->execdata;
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
context.previews = ntree->previews;
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
exec = ntreeShaderBeginExecTree_internal(&context, ntree, NODE_INSTANCE_KEY_BASE);
|
2018-06-08 08:07:48 +02:00
|
|
|
|
2013-03-18 16:34:57 +00: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
|
|
|
|
2011-09-05 21:01:50 +00:00
|
|
|
return exec;
|
|
|
|
}
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
void ntreeShaderEndExecTree_internal(bNodeTreeExec *exec)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
2013-03-18 16:34:57 +00:00
|
|
|
bNodeThreadStack *nts;
|
|
|
|
int a;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
if (exec->threadstack) {
|
2015-06-08 13:46:33 +02:00
|
|
|
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) {
|
2013-03-18 16:34:57 +00:00
|
|
|
MEM_freeN(nts->stack);
|
2019-04-22 13:31:31 +10:00
|
|
|
}
|
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
BLI_freelistN(&exec->threadstack[a]);
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
MEM_freeN(exec->threadstack);
|
|
|
|
exec->threadstack = NULL;
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
ntree_exec_end(exec);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ntreeShaderEndExecTree(bNodeTreeExec *exec)
|
|
|
|
{
|
|
|
|
if (exec) {
|
2013-04-04 14:07:10 +00:00
|
|
|
/* exec may get freed, so assign ntree */
|
|
|
|
bNodeTree *ntree = exec->nodetree;
|
2013-03-18 16:34:57 +00:00
|
|
|
ntreeShaderEndExecTree_internal(exec);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* XXX clear nodetree backpointer to exec data, same problem as noted in ntreeBeginExecTree */
|
2013-04-04 14:07:10 +00:00
|
|
|
ntree->execdata = NULL;
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
|
|
|
}
|