This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/intern/cycles/render/graph.cpp
Pascal Schoen 8825a8e951 Squashed commit of the following:
commit 90778901c9
Merge: 76eebd9 3bf0026
Author: Schoen <schoepas@deher1m1598.emea.adsint.biz>
Date:   Mon Apr 3 07:52:05 2017 +0200

    Merge branch 'master' into cycles_disney_brdf

commit 76eebd9379
Author: Schoen <schoepas@deher1m1598.emea.adsint.biz>
Date:   Thu Mar 30 15:34:20 2017 +0200

    Updated copyright for the new files.

commit 013f4a152a
Author: Schoen <schoepas@deher1m1598.emea.adsint.biz>
Date:   Thu Mar 30 15:32:55 2017 +0200

    Switched from multiplication of base and subsurface color to blending
    between them using the subsurface parameter.

commit 482ec5d1f2
Author: Schoen <schoepas@deher1m1598.emea.adsint.biz>
Date:   Mon Mar 13 15:47:12 2017 +0100

    Fixed a bug that caused an additional white diffuse closure call when using
    path tracing.

commit 26e906d162
Merge: 0593b8c 223aff9
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Feb 6 11:32:31 2017 +0100

    Merge branch 'master' into cycles_disney_brdf

commit 0593b8c51b
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Feb 6 11:30:36 2017 +0100

    Fixed the broken GLSL shader and implemented the Disney BRDF in the
    real-time view port.

commit 8c7e11423b
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Feb 3 14:24:05 2017 +0100

    Fix to comply strict compiler flags and some code cleanup

commit 17724e9d2d
Merge: 379ba34 520afa2
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Jan 24 09:59:58 2017 +0100

    Merge branch 'master' into cycles_disney_brdf

commit 379ba346b0
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Jan 24 09:28:56 2017 +0100

    Renamed the Disney BSDF to Principled BSDF.

commit f80dcb4f34
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Dec 2 13:55:12 2016 +0100

    Removed reflection call when roughness is low because of artifacts.

commit 732db8a57f
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed Nov 16 09:22:25 2016 +0100

    Indication if to use fresnel is now handled via the type of the BSDF.

commit 0103659f5e
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Nov 11 13:04:11 2016 +0100

    Fixed an error in the clearcoat where it appeared too bright for default
    light sources (like directional lights)

commit 0aa68f5335
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Nov 7 12:04:38 2016 +0100

    Resolved inconsistencies in using tabs and spaces

commit f5897a9494
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Nov 7 08:13:41 2016 +0100

    Improved the clearcoat part by using GTR1 instead of GTR2

commit 3dfc240e61
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Oct 31 11:31:36 2016 +0100

    Use reflection BSDF for glossy reflections when roughness is 0.0 to
    reduce computational expense and some code cleanup

    Code cleanup includes:
    - Code style cleanup and removed unused code
    - Consolidated code in the bsdf_microfacet_multi_impl.h to reduce
      some computational expense

commit a2dd0c5faf
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed Oct 26 08:51:10 2016 +0200

    Fixed glossy reflections and refractions for low roughness values and
    cleaned up the code.

    For low roughness values, the reflections had some strange behavior.

commit 9817375912
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Oct 25 12:37:40 2016 +0200

    Removed default values in setup functions and added extra functions for
    GGX with fresnel.

commit bbc5d9d452
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Oct 25 11:09:36 2016 +0200

    Switched from uniform to cosine hemisphere sampling for the diffuse and
    the sheen part.

commit d52d8f2813
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Oct 24 16:17:13 2016 +0200

    Removed the color parameters from the diffuse and sheen shader and use
    them as closure weights instead.

commit 8f3d927385
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Oct 24 09:57:06 2016 +0200

    Fixed the issue with artifacts when using anisotropy without linking the
    tangent input to a tangent node.

commit d93f680db9
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Oct 24 09:14:51 2016 +0200

    Added subsurface radius parameter to control the per color channel
    effection radius of the subsurface scattering.

commit c708c3e53b
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Oct 24 08:14:10 2016 +0200

    Rearranged the inputs of the shader.

commit dfbfff9c38
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Oct 21 09:27:05 2016 +0200

    Put spaces in the parameter names of the shader node

commit e5a748ced1
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Oct 21 08:51:20 2016 +0200

    Removed code that isn't in use anymore

commit 75992bebc1
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Oct 21 08:50:07 2016 +0200

    Code style cleanup

commit 4dfcf455f7
Merge: 243a0e3 2cd6a89
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Thu Oct 20 10:41:50 2016 +0200

    Merge branch 'master' into cycles_disney_brdf

commit 243a0e3eb8
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Thu Oct 20 10:01:45 2016 +0200

    Switching between OSL and SVM is more consistant now when using Disney
    BSDF.

    There were some minor differences in the OSL implementation, e.g. the
    refraction roughness was missing.

commit 2a5ac50922
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Sep 27 09:17:57 2016 +0200

    Fixed a bug that caused transparency to be always white when using OSL and
    selecting GGX as distribution of the Disney BSDF

commit e1fa862391
Merge: d0530a8 7f76f6f
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Sep 27 08:59:32 2016 +0200

    Merge branch 'master' into cycles_disney_brdf

commit d0530a8af0
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Sep 27 08:53:18 2016 +0200

    Cleanup the Disney BSDF implementation and removing unneeded files.

commit 3f4fc826bd
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Sep 27 08:36:07 2016 +0200

    Unified the OSL implementation of the Disney clearcoat as a simple
    microfacet shader like it was previously done in SVM

commit 4d3a0032ec
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Sep 26 12:35:36 2016 +0200

    Enhanced performance for Disney materials without subsurface scattering

commit 3cd5eb56cf
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Sep 16 08:47:56 2016 +0200

    Fixed a bug in the Disney BSDF that caused specular reflections to be too
    bright and diffuse is now reacting to the roughness again

    - A normalization for the fresnel was missing which caused the specular
      reflections to become too bright for the single-scatter GGX
    - The roughness value for the diffuse BSSRDF part has always been
      overwritten and thus always 0
    - Also the performance for refractive materials with roughness=0.0 has
      been improved

commit 7cb37d7119
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Thu Sep 8 12:24:43 2016 +0200

    Added selection field to the Disney BSDF node for switching between
    "Multiscatter GGX" and "GGX"

    In the "GGX" mode there is an additional parameter for changing the
    refraction roughness for materials with smooth surfaces and rough interns
    (e.g. honey). With the "Multiscatter GGX" this effect can't be produced at
    the moment and so here will be no separation of the two roughness values.

commit cdd29d06bb
Merge: 02c315a b40d1c1
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Sep 6 15:59:05 2016 +0200

    Merge branch 'master' into cycles_disney_brdf

commit 02c315aeb0
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Sep 6 15:16:09 2016 +0200

    Implemented the OSL part of the Disney shader

commit 5f880293ae
Merge: 630b80e b399a6d
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Sep 2 10:53:36 2016 +0200

    Merge branch 'master' into cycles_disney_brdf

commit 630b80e08b
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Sep 2 10:52:13 2016 +0200

    Fresnel in the microfacet multiscatter implementation improved

commit 0d9f4d7acb
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Aug 26 11:11:05 2016 +0200

    Fixed refraction roughness problem (refractions were always 100% rough)
    and set IOR of clearcoat to 1.5

commit 9eed34c7d9
Merge: ef29aae ae475e3
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Aug 16 15:22:32 2016 +0200

    Merge branch 'master' into cycles_disney_brdf

commit ef29aaee1a
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Aug 16 15:17:12 2016 +0200

    Implemented the fresnel in the multi-scatter GGX for the Disney BSDF

    - The specular/metallic part uses the multi-scatter GGX
    - The fresnel of the metallic part is controlled by the specular value
    - The color of the reflection part when using transparency can be
      controlled by the specularTint value

commit 88567af085
Merge: cc267e5 285e082
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed Aug 3 15:05:09 2016 +0200

    Merge branch 'master' into cycles_disney_brdf

commit cc267e52f2
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed Aug 3 15:00:25 2016 +0200

    Implemented the Disney clearcoat as a variation of the microfacet bsdf,
    removed the transparency roughness again and added an input for
    anisotropic rotations

commit 81f6c06b1f
Merge: ece5a08 7065022
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed Aug 3 11:42:02 2016 +0200

    Merge branch 'master' into cycles_disney_brdf

commit ece5a08e0d
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Jul 26 16:29:21 2016 +0200

    Base color now applied again to the refraction of transparent Disney
    materials

commit e3aff6849e
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Jul 26 16:05:19 2016 +0200

    Added subsurface color parameter to the Disney shader

commit b3ca6d8a2f
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Jul 26 12:30:25 2016 +0200

    Improvement of the SSS in the Disney shader

     * Now the bump normal is correctly used for the SSS.
     * SSS in Disney uses the Disney diffuse shader

commit d68729300e
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Jul 26 12:23:13 2016 +0200

    Better calculation of the Disney diffuse part

    Now the values for NdotL und NdotV are clamped to 0.0f for a better look
    when using normal maps

commit cb6e500b12
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Jul 25 16:26:42 2016 +0200

    Now one can disable specular reflactions again by setting specular and
    metallic to 0 (cracked this in the previous commit)

commit bfb9cb11b5
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Jul 25 16:11:07 2016 +0200

    fixed the Disney SSS and cleaned the initialization of the Disney shaders

commit 642c0fdad1
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Jul 25 16:09:55 2016 +0200

    fixed an error that was caused by the missing LABEL_REFLECT in the Disney
    diffuse shader

commit c10b484dca
Author: Jens Verwiebe <info@jensverwiebe.de>
Date:   Fri Jul 22 01:15:21 2016 +0200

    Rollback attempt to fix sss crashing, it prevented crash by disabling sss completely, thus useless

commit 462bba3f97
Author: Jens Verwiebe <info@jensverwiebe.de>
Date:   Thu Jul 21 23:11:59 2016 +0200

    Add an undef for sc_next for safety

commit 32d348577d
Author: Jens Verwiebe <info@jensverwiebe.de>
Date:   Thu Jul 21 00:15:48 2016 +0200

    Attempt to fix Disney SSS

commit dbad91ca6d
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed Jul 20 11:13:00 2016 +0200

    Added a roughness parameter for refractions (for scattering of the rays
    within an object)

    With this, one can create a translucent material with a smooth surface and
    with a milky look.

    The final refraction roughness has to be calculated using the surface
    roughness and the refraction roughness because those two are correlated
    for refractions. If a ray hits a rough surface of a translucent material,
    it is scattered while entering the surface. Then it is scattered further
    within the object. The calculation I'm using is the following:

    RefrRoughnessFinal = 1.0 - (1.0 - Roughness) * (1.0 - RefrRoughness)

commit 50ea5e3e34
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue Jun 7 10:24:50 2016 +0200

    Disney BSDF is now supporting CUDA

commit 10974cc826
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue May 31 11:18:07 2016 +0200

    Added parameters IOR and Transparency for refractions

    With this, the Disney BRDF/BSSRDF is extended by the BTDF part.

commit 218202c090
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon May 30 15:08:18 2016 +0200

    Added an additional normal for the clearcoat

    With this normal one can simulate a thin layer of clearcoat by applying a
    smoother normal map than the original to this input

commit dd139ead7e
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon May 30 12:40:56 2016 +0200

    Switched to the improved subsurface scattering from Christensen and
    Burley

commit 11160fa4e1
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon May 30 10:16:30 2016 +0200

    Added Disney Sheen shader as a preparation to get to a BSSRDF

commit cee4fe0cc9
Merge: 4f955d0 6b5bab6
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon May 30 09:08:09 2016 +0200

    Merge branch 'cycles_disney_brdf' of git.blender.org:blender into cycles_disney_brdf

    Conflicts:
    	intern/cycles/kernel/closure/bsdf_disney_clearcoat.h
    	intern/cycles/kernel/closure/bsdf_disney_diffuse.h
    	intern/cycles/kernel/closure/bsdf_disney_specular.h
    	intern/cycles/kernel/closure/bsdf_util.h
    	intern/cycles/kernel/osl/CMakeLists.txt
    	intern/cycles/kernel/osl/bsdf_disney_clearcoat.cpp
    	intern/cycles/kernel/osl/bsdf_disney_diffuse.cpp
    	intern/cycles/kernel/osl/bsdf_disney_specular.cpp
    	intern/cycles/kernel/osl/osl_closures.h
    	intern/cycles/kernel/shaders/node_disney_bsdf.osl
    	intern/cycles/render/nodes.cpp
    	intern/cycles/render/nodes.h

commit 4f955d0523
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue May 24 16:38:23 2016 +0200

    SVM and OSL are both working for the simple version of the Disney BRDF

commit 1f5c41874b
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue May 24 09:58:50 2016 +0200

    Disney node can be used without SVM and started to cleanup the OSL implementation

    There is still some wrong behavior for SVM for the Schlick Fresnel part at the
    specular and clearcoat

commit d4b814e930
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed May 18 10:22:29 2016 +0200

    Switched from a parameter struct for Disney parameters to ShaderClosure params

commit b86a1f5ba5
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed May 18 10:19:57 2016 +0200

    Added additional variables for storing parameters in the ShaderClosure struct

commit 585b886236
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue May 17 12:03:17 2016 +0200

    added output parameter to the DisneyBsdfNode

    That has been forgotten after removing the inheritance of BsdfNode

commit f91a286398
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue May 17 10:40:48 2016 +0200

    removed BsdfNode class inheritance for DisneyBsdfNode

    That's due to a naming difference. The Disney BSDF uses the name 'Base Color'
    while the BsdfNode had a 'Color' input. That caused a text message to be
    printed while rendering.

commit 30da91c9c5
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed May 4 16:08:10 2016 +0200

    disney implementation cleaned

commit 30d41da0f0
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed May 4 13:23:07 2016 +0200

    added the disney brdf as a shader node

commit 1f099fce24
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue May 3 16:54:49 2016 +0200

    added clearcoat implementation

commit 00a1378b98
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Apr 29 22:56:49 2016 +0200

    disney diffuse und specular implemented

commit 6baa7a7eb7
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Apr 18 15:21:32 2016 +0200

    disney diffuse is working correctly

commit d8fa169bf3
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Apr 18 08:41:53 2016 +0200

    added vessel for disney diffuse shader

commit 6b5bab6cec
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed May 18 10:22:29 2016 +0200

    Switched from a parameter struct for Disney parameters to ShaderClosure params

commit f6499c2676
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed May 18 10:19:57 2016 +0200

    Added additional variables for storing parameters in the ShaderClosure struct

commit 7100640b65
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue May 17 12:03:17 2016 +0200

    added output parameter to the DisneyBsdfNode

    That has been forgotten after removing the inheritance of BsdfNode

commit 419ee54411
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue May 17 10:40:48 2016 +0200

    removed BsdfNode class inheritance for DisneyBsdfNode

    That's due to a naming difference. The Disney BSDF uses the name 'Base Color'
    while the BsdfNode had a 'Color' input. That caused a text message to be
    printed while rendering.

commit 6006f91e87
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed May 4 16:08:10 2016 +0200

    disney implementation cleaned

commit 0ed0895914
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Wed May 4 13:23:07 2016 +0200

    added the disney brdf as a shader node

commit 0630b742d7
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Tue May 3 16:54:49 2016 +0200

    added clearcoat implementation

commit 9f3d39744b
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Fri Apr 29 22:56:49 2016 +0200

    disney diffuse und specular implemented

commit 9b26206376
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Apr 18 15:21:32 2016 +0200

    disney diffuse is working correctly

commit 4711a3927d
Author: Pascal Schoen <pascal_schoen@gmx.net>
Date:   Mon Apr 18 08:41:53 2016 +0200

    added vessel for disney diffuse shader

Differential Revision: https://developer.blender.org/D2313
2017-04-18 11:43:09 +02:00

1124 lines
29 KiB
C++

/*
* Copyright 2011-2016 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "render/attribute.h"
#include "render/graph.h"
#include "render/nodes.h"
#include "render/scene.h"
#include "render/shader.h"
#include "render/constant_fold.h"
#include "util/util_algorithm.h"
#include "util/util_debug.h"
#include "util/util_foreach.h"
#include "util/util_queue.h"
#include "util/util_logging.h"
CCL_NAMESPACE_BEGIN
namespace {
bool check_node_inputs_has_links(const ShaderNode *node)
{
foreach(const ShaderInput *in, node->inputs) {
if(in->link) {
return true;
}
}
return false;
}
bool check_node_inputs_traversed(const ShaderNode *node,
const ShaderNodeSet& done)
{
foreach(const ShaderInput *in, node->inputs) {
if(in->link) {
if(done.find(in->link->parent) == done.end()) {
return false;
}
}
}
return true;
}
} /* namespace */
/* Node */
ShaderNode::ShaderNode(const NodeType *type)
: Node(type)
{
name = type->name;
id = -1;
bump = SHADER_BUMP_NONE;
special_type = SHADER_SPECIAL_TYPE_NONE;
create_inputs_outputs(type);
}
ShaderNode::~ShaderNode()
{
foreach(ShaderInput *socket, inputs)
delete socket;
foreach(ShaderOutput *socket, outputs)
delete socket;
}
void ShaderNode::create_inputs_outputs(const NodeType *type)
{
foreach(const SocketType& socket, type->inputs) {
if(socket.flags & SocketType::LINKABLE) {
inputs.push_back(new ShaderInput(socket, this));
}
}
foreach(const SocketType& socket, type->outputs) {
outputs.push_back(new ShaderOutput(socket, this));
}
}
ShaderInput *ShaderNode::input(const char *name)
{
foreach(ShaderInput *socket, inputs) {
if(socket->name() == name)
return socket;
}
return NULL;
}
ShaderOutput *ShaderNode::output(const char *name)
{
foreach(ShaderOutput *socket, outputs)
if(socket->name() == name)
return socket;
return NULL;
}
ShaderInput *ShaderNode::input(ustring name)
{
foreach(ShaderInput *socket, inputs) {
if(socket->name() == name)
return socket;
}
return NULL;
}
ShaderOutput *ShaderNode::output(ustring name)
{
foreach(ShaderOutput *socket, outputs)
if(socket->name() == name)
return socket;
return NULL;
}
void ShaderNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
foreach(ShaderInput *input, inputs) {
if(!input->link) {
if(input->flags() & SocketType::LINK_TEXTURE_GENERATED) {
if(shader->has_surface)
attributes->add(ATTR_STD_GENERATED);
if(shader->has_volume)
attributes->add(ATTR_STD_GENERATED_TRANSFORM);
}
else if(input->flags() & SocketType::LINK_TEXTURE_UV) {
if(shader->has_surface)
attributes->add(ATTR_STD_UV);
}
}
}
}
bool ShaderNode::equals(const ShaderNode& other)
{
if(type != other.type || bump != other.bump) {
return false;
}
assert(inputs.size() == other.inputs.size());
/* Compare unlinkable sockets */
foreach(const SocketType& socket, type->inputs) {
if(!(socket.flags & SocketType::LINKABLE)) {
if(!Node::equals_value(other, socket)) {
return false;
}
}
}
/* Compare linkable input sockets */
for(int i = 0; i < inputs.size(); ++i) {
ShaderInput *input_a = inputs[i],
*input_b = other.inputs[i];
if(input_a->link == NULL && input_b->link == NULL) {
/* Unconnected inputs are expected to have the same value. */
if(!Node::equals_value(other, input_a->socket_type)) {
return false;
}
}
else if(input_a->link != NULL && input_b->link != NULL) {
/* Expect links are to come from the same exact socket. */
if(input_a->link != input_b->link) {
return false;
}
}
else {
/* One socket has a link and another has not, inputs can't be
* considered equal.
*/
return false;
}
}
return true;
}
/* Graph */
ShaderGraph::ShaderGraph()
{
finalized = false;
simplified = false;
num_node_ids = 0;
add(new OutputNode());
}
ShaderGraph::~ShaderGraph()
{
clear_nodes();
}
ShaderNode *ShaderGraph::add(ShaderNode *node)
{
assert(!finalized);
simplified = false;
node->id = num_node_ids++;
nodes.push_back(node);
return node;
}
OutputNode *ShaderGraph::output()
{
return (OutputNode*)nodes.front();
}
ShaderGraph *ShaderGraph::copy()
{
ShaderGraph *newgraph = new ShaderGraph();
/* copy nodes */
ShaderNodeSet nodes_all;
foreach(ShaderNode *node, nodes)
nodes_all.insert(node);
ShaderNodeMap nodes_copy;
copy_nodes(nodes_all, nodes_copy);
/* add nodes (in same order, so output is still first) */
newgraph->clear_nodes();
foreach(ShaderNode *node, nodes)
newgraph->add(nodes_copy[node]);
newgraph->simplified = simplified;
return newgraph;
}
void ShaderGraph::connect(ShaderOutput *from, ShaderInput *to)
{
assert(!finalized);
assert(from && to);
if(to->link) {
fprintf(stderr, "Cycles shader graph connect: input already connected.\n");
return;
}
if(from->type() != to->type()) {
/* for closures we can't do automatic conversion */
if(from->type() == SocketType::CLOSURE || to->type() == SocketType::CLOSURE) {
fprintf(stderr, "Cycles shader graph connect: can only connect closure to closure "
"(%s.%s to %s.%s).\n",
from->parent->name.c_str(), from->name().c_str(),
to->parent->name.c_str(), to->name().c_str());
return;
}
/* add automatic conversion node in case of type mismatch */
ShaderNode *convert = add(new ConvertNode(from->type(), to->type(), true));
connect(from, convert->inputs[0]);
connect(convert->outputs[0], to);
}
else {
/* types match, just connect */
to->link = from;
from->links.push_back(to);
}
}
void ShaderGraph::disconnect(ShaderOutput *from)
{
assert(!finalized);
simplified = false;
foreach(ShaderInput *sock, from->links) {
sock->link = NULL;
}
from->links.clear();
}
void ShaderGraph::disconnect(ShaderInput *to)
{
assert(!finalized);
assert(to->link);
simplified = false;
ShaderOutput *from = to->link;
to->link = NULL;
from->links.erase(remove(from->links.begin(), from->links.end(), to), from->links.end());
}
void ShaderGraph::relink(ShaderNode *node, ShaderOutput *from, ShaderOutput *to)
{
simplified = false;
/* Copy because disconnect modifies this list */
vector<ShaderInput*> outputs = from->links;
/* Bypass node by moving all links from "from" to "to" */
foreach(ShaderInput *sock, node->inputs) {
if(sock->link)
disconnect(sock);
}
foreach(ShaderInput *sock, outputs) {
disconnect(sock);
if(to)
connect(to, sock);
}
}
void ShaderGraph::simplify(Scene *scene)
{
if(!simplified) {
default_inputs(scene->shader_manager->use_osl());
clean(scene);
refine_bump_nodes();
simplified = true;
}
}
void ShaderGraph::finalize(Scene *scene,
bool do_bump,
bool do_simplify,
bool bump_in_object_space)
{
/* before compiling, the shader graph may undergo a number of modifications.
* currently we set default geometry shader inputs, and create automatic bump
* from displacement. a graph can be finalized only once, and should not be
* modified afterwards. */
if(!finalized) {
simplify(scene);
if(do_bump)
bump_from_displacement(bump_in_object_space);
ShaderInput *surface_in = output()->input("Surface");
ShaderInput *volume_in = output()->input("Volume");
/* todo: make this work when surface and volume closures are tangled up */
if(surface_in->link)
transform_multi_closure(surface_in->link->parent, NULL, false);
if(volume_in->link)
transform_multi_closure(volume_in->link->parent, NULL, true);
finalized = true;
}
else if(do_simplify) {
simplify_settings(scene);
}
}
void ShaderGraph::find_dependencies(ShaderNodeSet& dependencies, ShaderInput *input)
{
/* find all nodes that this input depends on directly and indirectly */
ShaderNode *node = (input->link)? input->link->parent: NULL;
if(node != NULL && dependencies.find(node) == dependencies.end()) {
foreach(ShaderInput *in, node->inputs)
find_dependencies(dependencies, in);
dependencies.insert(node);
}
}
void ShaderGraph::clear_nodes()
{
foreach(ShaderNode *node, nodes) {
delete node;
}
nodes.clear();
}
void ShaderGraph::copy_nodes(ShaderNodeSet& nodes, ShaderNodeMap& nnodemap)
{
/* copy a set of nodes, and the links between them. the assumption is
* made that all nodes that inputs are linked to are in the set too. */
/* copy nodes */
foreach(ShaderNode *node, nodes) {
ShaderNode *nnode = node->clone();
nnodemap[node] = nnode;
/* create new inputs and outputs to recreate links and ensure
* that we still point to valid SocketType if the NodeType
* changed in cloning, as it does for OSL nodes */
nnode->inputs.clear();
nnode->outputs.clear();
nnode->create_inputs_outputs(nnode->type);
}
/* recreate links */
foreach(ShaderNode *node, nodes) {
foreach(ShaderInput *input, node->inputs) {
if(input->link) {
/* find new input and output */
ShaderNode *nfrom = nnodemap[input->link->parent];
ShaderNode *nto = nnodemap[input->parent];
ShaderOutput *noutput = nfrom->output(input->link->name());
ShaderInput *ninput = nto->input(input->name());
/* connect */
connect(noutput, ninput);
}
}
}
}
/* Graph simplification */
/* ******************** */
/* Remove proxy nodes.
*
* These only exists temporarily when exporting groups, and we must remove them
* early so that node->attributes() and default links do not see them.
*/
void ShaderGraph::remove_proxy_nodes()
{
vector<bool> removed(num_node_ids, false);
bool any_node_removed = false;
foreach(ShaderNode *node, nodes) {
if(node->special_type == SHADER_SPECIAL_TYPE_PROXY) {
ConvertNode *proxy = static_cast<ConvertNode*>(node);
ShaderInput *input = proxy->inputs[0];
ShaderOutput *output = proxy->outputs[0];
/* bypass the proxy node */
if(input->link) {
relink(proxy, output, input->link);
}
else {
/* Copy because disconnect modifies this list */
vector<ShaderInput*> links(output->links);
foreach(ShaderInput *to, links) {
/* remove any autoconvert nodes too if they lead to
* sockets with an automatically set default value */
ShaderNode *tonode = to->parent;
if(tonode->special_type == SHADER_SPECIAL_TYPE_AUTOCONVERT) {
bool all_links_removed = true;
vector<ShaderInput*> links = tonode->outputs[0]->links;
foreach(ShaderInput *autoin, links) {
if(autoin->flags() & SocketType::DEFAULT_LINK_MASK)
disconnect(autoin);
else
all_links_removed = false;
}
if(all_links_removed)
removed[tonode->id] = true;
}
disconnect(to);
/* transfer the default input value to the target socket */
tonode->copy_value(to->socket_type, *proxy, input->socket_type);
}
}
removed[proxy->id] = true;
any_node_removed = true;
}
}
/* remove nodes */
if(any_node_removed) {
list<ShaderNode*> newnodes;
foreach(ShaderNode *node, nodes) {
if(!removed[node->id])
newnodes.push_back(node);
else
delete node;
}
nodes = newnodes;
}
}
/* Constant folding.
*
* Try to constant fold some nodes, and pipe result directly to
* the input socket of connected nodes.
*/
void ShaderGraph::constant_fold()
{
ShaderNodeSet done, scheduled;
queue<ShaderNode*> traverse_queue;
bool has_displacement = (output()->input("Displacement")->link != NULL);
/* Schedule nodes which doesn't have any dependencies. */
foreach(ShaderNode *node, nodes) {
if(!check_node_inputs_has_links(node)) {
traverse_queue.push(node);
scheduled.insert(node);
}
}
while(!traverse_queue.empty()) {
ShaderNode *node = traverse_queue.front();
traverse_queue.pop();
done.insert(node);
foreach(ShaderOutput *output, node->outputs) {
if(output->links.size() == 0) {
continue;
}
/* Schedule node which was depending on the value,
* when possible. Do it before disconnect.
*/
foreach(ShaderInput *input, output->links) {
if(scheduled.find(input->parent) != scheduled.end()) {
/* Node might not be optimized yet but scheduled already
* by other dependencies. No need to re-schedule it.
*/
continue;
}
/* Schedule node if its inputs are fully done. */
if(check_node_inputs_traversed(input->parent, done)) {
traverse_queue.push(input->parent);
scheduled.insert(input->parent);
}
}
/* Optimize current node. */
ConstantFolder folder(this, node, output);
node->constant_fold(folder);
}
}
/* Folding might have removed all nodes connected to the displacement output
* even tho there is displacement to be applied, so add in a value node if
* that happens to ensure there is still a valid graph for displacement.
*/
if(has_displacement && !output()->input("Displacement")->link) {
ValueNode *value = (ValueNode*)add(new ValueNode());
value->value = output()->displacement;
connect(value->output("Value"), output()->input("Displacement"));
}
}
/* Simplification. */
void ShaderGraph::simplify_settings(Scene *scene)
{
foreach(ShaderNode *node, nodes) {
node->simplify_settings(scene);
}
}
/* Deduplicate nodes with same settings. */
void ShaderGraph::deduplicate_nodes()
{
/* NOTES:
* - Deduplication happens for nodes which has same exact settings and same
* exact input links configuration (either connected to same output or has
* the same exact default value).
* - Deduplication happens in the bottom-top manner, so we know for fact that
* all traversed nodes are either can not be deduplicated at all or were
* already deduplicated.
*/
ShaderNodeSet scheduled, done;
map<ustring, ShaderNodeSet> candidates;
queue<ShaderNode*> traverse_queue;
int num_deduplicated = 0;
/* Schedule nodes which doesn't have any dependencies. */
foreach(ShaderNode *node, nodes) {
if(!check_node_inputs_has_links(node)) {
traverse_queue.push(node);
scheduled.insert(node);
}
}
while(!traverse_queue.empty()) {
ShaderNode *node = traverse_queue.front();
traverse_queue.pop();
done.insert(node);
/* Schedule the nodes which were depending on the current node. */
bool has_output_links = false;
foreach(ShaderOutput *output, node->outputs) {
foreach(ShaderInput *input, output->links) {
has_output_links = true;
if(scheduled.find(input->parent) != scheduled.end()) {
/* Node might not be optimized yet but scheduled already
* by other dependencies. No need to re-schedule it.
*/
continue;
}
/* Schedule node if its inputs are fully done. */
if(check_node_inputs_traversed(input->parent, done)) {
traverse_queue.push(input->parent);
scheduled.insert(input->parent);
}
}
}
/* Only need to care about nodes that are actually used */
if(!has_output_links) {
continue;
}
/* Try to merge this node with another one. */
ShaderNode *merge_with = NULL;
foreach(ShaderNode *other_node, candidates[node->type->name]) {
if(node != other_node && node->equals(*other_node)) {
merge_with = other_node;
break;
}
}
/* If found an equivalent, merge; otherwise keep node for later merges */
if(merge_with != NULL) {
for(int i = 0; i < node->outputs.size(); ++i) {
relink(node, node->outputs[i], merge_with->outputs[i]);
}
num_deduplicated++;
}
else {
candidates[node->type->name].insert(node);
}
}
if(num_deduplicated > 0) {
VLOG(1) << "Deduplicated " << num_deduplicated << " nodes.";
}
}
/* Check whether volume output has meaningful nodes, otherwise
* disconnect the output.
*/
void ShaderGraph::verify_volume_output()
{
/* Check whether we can optimize the whole volume graph out. */
ShaderInput *volume_in = output()->input("Volume");
if(volume_in->link == NULL) {
return;
}
bool has_valid_volume = false;
ShaderNodeSet scheduled;
queue<ShaderNode*> traverse_queue;
/* Schedule volume output. */
traverse_queue.push(volume_in->link->parent);
scheduled.insert(volume_in->link->parent);
/* Traverse down the tree. */
while(!traverse_queue.empty()) {
ShaderNode *node = traverse_queue.front();
traverse_queue.pop();
/* Node is fully valid for volume, can't optimize anything out. */
if(node->has_volume_support()) {
has_valid_volume = true;
break;
}
foreach(ShaderInput *input, node->inputs) {
if(input->link == NULL) {
continue;
}
if(scheduled.find(input->link->parent) != scheduled.end()) {
continue;
}
traverse_queue.push(input->link->parent);
scheduled.insert(input->link->parent);
}
}
if(!has_valid_volume) {
VLOG(1) << "Disconnect meaningless volume output.";
disconnect(volume_in->link);
}
}
void ShaderGraph::break_cycles(ShaderNode *node, vector<bool>& visited, vector<bool>& on_stack)
{
visited[node->id] = true;
on_stack[node->id] = true;
foreach(ShaderInput *input, node->inputs) {
if(input->link) {
ShaderNode *depnode = input->link->parent;
if(on_stack[depnode->id]) {
/* break cycle */
disconnect(input);
fprintf(stderr, "Cycles shader graph: detected cycle in graph, connection removed.\n");
}
else if(!visited[depnode->id]) {
/* visit dependencies */
break_cycles(depnode, visited, on_stack);
}
}
}
on_stack[node->id] = false;
}
void ShaderGraph::clean(Scene *scene)
{
/* Graph simplification */
/* NOTE: Remove proxy nodes was already done. */
constant_fold();
simplify_settings(scene);
deduplicate_nodes();
verify_volume_output();
/* we do two things here: find cycles and break them, and remove unused
* nodes that don't feed into the output. how cycles are broken is
* undefined, they are invalid input, the important thing is to not crash */
vector<bool> visited(num_node_ids, false);
vector<bool> on_stack(num_node_ids, false);
/* break cycles */
break_cycles(output(), visited, on_stack);
/* disconnect unused nodes */
foreach(ShaderNode *node, nodes) {
if(!visited[node->id]) {
foreach(ShaderInput *to, node->inputs) {
ShaderOutput *from = to->link;
if(from) {
to->link = NULL;
from->links.erase(remove(from->links.begin(), from->links.end(), to), from->links.end());
}
}
}
}
/* remove unused nodes */
list<ShaderNode*> newnodes;
foreach(ShaderNode *node, nodes) {
if(visited[node->id])
newnodes.push_back(node);
else
delete node;
}
nodes = newnodes;
}
void ShaderGraph::default_inputs(bool do_osl)
{
/* nodes can specify default texture coordinates, for now we give
* everything the position by default, except for the sky texture */
ShaderNode *geom = NULL;
ShaderNode *texco = NULL;
foreach(ShaderNode *node, nodes) {
foreach(ShaderInput *input, node->inputs) {
if(!input->link && (!(input->flags() & SocketType::OSL_INTERNAL) || do_osl)) {
if(input->flags() & SocketType::LINK_TEXTURE_GENERATED) {
if(!texco)
texco = new TextureCoordinateNode();
connect(texco->output("Generated"), input);
}
else if(input->flags() & SocketType::LINK_TEXTURE_UV) {
if(!texco)
texco = new TextureCoordinateNode();
connect(texco->output("UV"), input);
}
else if(input->flags() & SocketType::LINK_INCOMING) {
if(!geom)
geom = new GeometryNode();
connect(geom->output("Incoming"), input);
}
else if(input->flags() & SocketType::LINK_NORMAL) {
if(!geom)
geom = new GeometryNode();
connect(geom->output("Normal"), input);
}
else if(input->flags() & SocketType::LINK_POSITION) {
if(!geom)
geom = new GeometryNode();
connect(geom->output("Position"), input);
}
else if(input->flags() & SocketType::LINK_TANGENT) {
if(!geom)
geom = new GeometryNode();
connect(geom->output("Tangent"), input);
}
}
}
}
if(geom)
add(geom);
if(texco)
add(texco);
}
void ShaderGraph::refine_bump_nodes()
{
/* we transverse the node graph looking for bump nodes, when we find them,
* like in bump_from_displacement(), we copy the sub-graph defined from "bump"
* input to the inputs "center","dx" and "dy" What is in "bump" input is moved
* to "center" input. */
foreach(ShaderNode *node, nodes) {
if(node->special_type == SHADER_SPECIAL_TYPE_BUMP && node->input("Height")->link) {
ShaderInput *bump_input = node->input("Height");
ShaderNodeSet nodes_bump;
/* make 2 extra copies of the subgraph defined in Bump input */
ShaderNodeMap nodes_dx;
ShaderNodeMap nodes_dy;
/* find dependencies for the given input */
find_dependencies(nodes_bump, bump_input);
copy_nodes(nodes_bump, nodes_dx);
copy_nodes(nodes_bump, nodes_dy);
/* mark nodes to indicate they are use for bump computation, so
that any texture coordinates are shifted by dx/dy when sampling */
foreach(ShaderNode *node, nodes_bump)
node->bump = SHADER_BUMP_CENTER;
foreach(NodePair& pair, nodes_dx)
pair.second->bump = SHADER_BUMP_DX;
foreach(NodePair& pair, nodes_dy)
pair.second->bump = SHADER_BUMP_DY;
ShaderOutput *out = bump_input->link;
ShaderOutput *out_dx = nodes_dx[out->parent]->output(out->name());
ShaderOutput *out_dy = nodes_dy[out->parent]->output(out->name());
connect(out_dx, node->input("SampleX"));
connect(out_dy, node->input("SampleY"));
/* add generated nodes */
foreach(NodePair& pair, nodes_dx)
add(pair.second);
foreach(NodePair& pair, nodes_dy)
add(pair.second);
/* connect what is connected is bump to samplecenter input*/
connect(out , node->input("SampleCenter"));
/* bump input is just for connectivity purpose for the graph input,
* we re-connected this input to samplecenter, so lets disconnect it
* from bump input */
disconnect(bump_input);
}
}
}
void ShaderGraph::bump_from_displacement(bool use_object_space)
{
/* generate bump mapping automatically from displacement. bump mapping is
* done using a 3-tap filter, computing the displacement at the center,
* and two other positions shifted by ray differentials.
*
* since the input to displacement is a node graph, we need to ensure that
* all texture coordinates use are shift by the ray differentials. for this
* reason we make 3 copies of the node subgraph defining the displacement,
* with each different geometry and texture coordinate nodes that generate
* different shifted coordinates.
*
* these 3 displacement values are then fed into the bump node, which will
* output the perturbed normal. */
ShaderInput *displacement_in = output()->input("Displacement");
if(!displacement_in->link)
return;
/* find dependencies for the given input */
ShaderNodeSet nodes_displace;
find_dependencies(nodes_displace, displacement_in);
/* copy nodes for 3 bump samples */
ShaderNodeMap nodes_center;
ShaderNodeMap nodes_dx;
ShaderNodeMap nodes_dy;
copy_nodes(nodes_displace, nodes_center);
copy_nodes(nodes_displace, nodes_dx);
copy_nodes(nodes_displace, nodes_dy);
/* mark nodes to indicate they are use for bump computation, so
* that any texture coordinates are shifted by dx/dy when sampling */
foreach(NodePair& pair, nodes_center)
pair.second->bump = SHADER_BUMP_CENTER;
foreach(NodePair& pair, nodes_dx)
pair.second->bump = SHADER_BUMP_DX;
foreach(NodePair& pair, nodes_dy)
pair.second->bump = SHADER_BUMP_DY;
/* add set normal node and connect the bump normal ouput to the set normal
* output, so it can finally set the shader normal, note we are only doing
* this for bump from displacement, this will be the only bump allowed to
* overwrite the shader normal */
ShaderNode *set_normal = add(new SetNormalNode());
/* add bump node and connect copied graphs to it */
BumpNode *bump = (BumpNode*)add(new BumpNode());
bump->use_object_space = use_object_space;
ShaderOutput *out = displacement_in->link;
ShaderOutput *out_center = nodes_center[out->parent]->output(out->name());
ShaderOutput *out_dx = nodes_dx[out->parent]->output(out->name());
ShaderOutput *out_dy = nodes_dy[out->parent]->output(out->name());
connect(out_center, bump->input("SampleCenter"));
connect(out_dx, bump->input("SampleX"));
connect(out_dy, bump->input("SampleY"));
/* connect the bump out to the set normal in: */
connect(bump->output("Normal"), set_normal->input("Direction"));
/* connect to output node */
connect(set_normal->output("Normal"), output()->input("Normal"));
/* finally, add the copied nodes to the graph. we can't do this earlier
* because we would create dependency cycles in the above loop */
foreach(NodePair& pair, nodes_center)
add(pair.second);
foreach(NodePair& pair, nodes_dx)
add(pair.second);
foreach(NodePair& pair, nodes_dy)
add(pair.second);
}
void ShaderGraph::transform_multi_closure(ShaderNode *node, ShaderOutput *weight_out, bool volume)
{
/* for SVM in multi closure mode, this transforms the shader mix/add part of
* the graph into nodes that feed weights into closure nodes. this is too
* avoid building a closure tree and then flattening it, and instead write it
* directly to an array */
if(node->special_type == SHADER_SPECIAL_TYPE_COMBINE_CLOSURE) {
ShaderInput *fin = node->input("Fac");
ShaderInput *cl1in = node->input("Closure1");
ShaderInput *cl2in = node->input("Closure2");
ShaderOutput *weight1_out, *weight2_out;
if(fin) {
/* mix closure: add node to mix closure weights */
MixClosureWeightNode *mix_node = new MixClosureWeightNode();
add(mix_node);
ShaderInput *fac_in = mix_node->input("Fac");
ShaderInput *weight_in = mix_node->input("Weight");
if(fin->link)
connect(fin->link, fac_in);
else
mix_node->fac = node->get_float(fin->socket_type);
if(weight_out)
connect(weight_out, weight_in);
weight1_out = mix_node->output("Weight1");
weight2_out = mix_node->output("Weight2");
}
else {
/* add closure: just pass on any weights */
weight1_out = weight_out;
weight2_out = weight_out;
}
if(cl1in->link)
transform_multi_closure(cl1in->link->parent, weight1_out, volume);
if(cl2in->link)
transform_multi_closure(cl2in->link->parent, weight2_out, volume);
}
else {
ShaderInput *weight_in = node->input((volume)? "VolumeMixWeight": "SurfaceMixWeight");
/* not a closure node? */
if(!weight_in)
return;
/* already has a weight connected to it? add weights */
float weight_value = node->get_float(weight_in->socket_type);
if(weight_in->link || weight_value != 0.0f) {
MathNode *math_node = new MathNode();
add(math_node);
if(weight_in->link)
connect(weight_in->link, math_node->input("Value1"));
else
math_node->value1 = weight_value;
if(weight_out)
connect(weight_out, math_node->input("Value2"));
else
math_node->value2 = 1.0f;
weight_out = math_node->output("Value");
if(weight_in->link)
disconnect(weight_in);
}
/* connected to closure mix weight */
if(weight_out)
connect(weight_out, weight_in);
else
node->set(weight_in->socket_type, weight_value + 1.0f);
}
}
int ShaderGraph::get_num_closures()
{
int num_closures = 0;
foreach(ShaderNode *node, nodes) {
ClosureType closure_type = node->get_closure_type();
if(closure_type == CLOSURE_NONE_ID) {
continue;
}
else if(CLOSURE_IS_BSSRDF(closure_type)) {
num_closures += 3;
}
else if(CLOSURE_IS_GLASS(closure_type)) {
num_closures += 2;
}
else if(CLOSURE_IS_BSDF_MULTISCATTER(closure_type)) {
num_closures += 2;
}
else if(CLOSURE_IS_PRINCIPLED(closure_type)) {
num_closures += 8;
}
else {
++num_closures;
}
}
return num_closures;
}
void ShaderGraph::dump_graph(const char *filename)
{
FILE *fd = fopen(filename, "w");
if(fd == NULL) {
printf("Error opening file for dumping the graph: %s\n", filename);
return;
}
fprintf(fd, "digraph shader_graph {\n");
fprintf(fd, "ranksep=1.5\n");
fprintf(fd, "rankdir=LR\n");
fprintf(fd, "splines=false\n");
foreach(ShaderNode *node, nodes) {
fprintf(fd, "// NODE: %p\n", node);
fprintf(fd, "\"%p\" [shape=record,label=\"{", node);
if(node->inputs.size()) {
fprintf(fd, "{");
foreach(ShaderInput *socket, node->inputs) {
if(socket != node->inputs[0]) {
fprintf(fd, "|");
}
fprintf(fd, "<IN_%p>%s", socket, socket->name().c_str());
}
fprintf(fd, "}|");
}
fprintf(fd, "%s", node->name.c_str());
if(node->bump == SHADER_BUMP_CENTER) {
fprintf(fd, " (bump:center)");
}
else if(node->bump == SHADER_BUMP_DX) {
fprintf(fd, " (bump:dx)");
}
else if(node->bump == SHADER_BUMP_DY) {
fprintf(fd, " (bump:dy)");
}
if(node->outputs.size()) {
fprintf(fd, "|{");
foreach(ShaderOutput *socket, node->outputs) {
if(socket != node->outputs[0]) {
fprintf(fd, "|");
}
fprintf(fd, "<OUT_%p>%s", socket, socket->name().c_str());
}
fprintf(fd, "}");
}
fprintf(fd, "}\"]");
}
foreach(ShaderNode *node, nodes) {
foreach(ShaderOutput *output, node->outputs) {
foreach(ShaderInput *input, output->links) {
fprintf(fd,
"// CONNECTION: OUT_%p->IN_%p (%s:%s)\n",
output,
input,
output->name().c_str(), input->name().c_str());
fprintf(fd,
"\"%p\":\"OUT_%p\":e -> \"%p\":\"IN_%p\":w [label=\"\"]\n",
output->parent,
output,
input->parent,
input);
}
}
}
fprintf(fd, "}\n");
fclose(fd);
}
CCL_NAMESPACE_END