From bdda0964e0a5180bd0bc4fb8e38dbe2198bd9a9a Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 18 Jul 2018 13:03:09 +0200 Subject: Compositor: Cryptomatte compositing node. This patch adds a new matte node that implements the Cryptomatte specification. It also incluces a custom eye dropper that works outside of a color picker. Cryptomatte export for the Cycles render engine will be in a separate patch. Reviewers: brecht Reviewed By: brecht Subscribers: brecht Tags: #compositing Differential Revision: https://developer.blender.org/D3531 --- source/blender/blenkernel/BKE_node.h | 6 + source/blender/blenkernel/intern/node.c | 1 + source/blender/blenlib/BLI_hash_mm3.h | 40 +++ source/blender/blenlib/CMakeLists.txt | 2 + source/blender/blenlib/intern/hash_mm3.c | 147 ++++++++++ source/blender/blenloader/intern/readfile.c | 4 + source/blender/blenloader/intern/writefile.c | 7 + source/blender/compositor/CMakeLists.txt | 5 + source/blender/compositor/intern/COM_Converter.cpp | 4 + .../compositor/nodes/COM_CryptomatteNode.cpp | 120 ++++++++ .../blender/compositor/nodes/COM_CryptomatteNode.h | 38 +++ .../operations/COM_CryptomatteOperation.cpp | 75 +++++ .../operations/COM_CryptomatteOperation.h | 40 +++ source/blender/editors/include/UI_interface.h | 1 + .../editors/interface/interface_eyedropper.c | 1 + .../editors/interface/interface_eyedropper_color.c | 89 +++--- .../blender/editors/interface/interface_intern.h | 1 + source/blender/editors/interface/interface_ops.c | 1 + .../editors/interface/interface_templates.c | 18 ++ source/blender/editors/space_node/drawnode.c | 23 ++ source/blender/editors/space_node/node_edit.c | 90 ++++++ source/blender/editors/space_node/node_intern.h | 3 + source/blender/editors/space_node/node_ops.c | 3 + source/blender/makesdna/DNA_node_types.h | 8 + source/blender/makesrna/intern/rna_nodetree.c | 68 +++++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_composite.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../composite/nodes/node_composite_cryptomatte.c | 309 +++++++++++++++++++++ 29 files changed, 1065 insertions(+), 42 deletions(-) create mode 100644 source/blender/blenlib/BLI_hash_mm3.h create mode 100644 source/blender/blenlib/intern/hash_mm3.c create mode 100644 source/blender/compositor/nodes/COM_CryptomatteNode.cpp create mode 100644 source/blender/compositor/nodes/COM_CryptomatteNode.h create mode 100644 source/blender/compositor/operations/COM_CryptomatteOperation.cpp create mode 100644 source/blender/compositor/operations/COM_CryptomatteOperation.h create mode 100644 source/blender/nodes/composite/nodes/node_composite_cryptomatte.c (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index eca7f82541f..8e54c6a87c4 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -945,6 +945,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria #define CMP_NODE_PLANETRACKDEFORM 320 #define CMP_NODE_CORNERPIN 321 #define CMP_NODE_SWITCH_VIEW 322 +#define CMP_NODE_CRYPTOMATTE 323 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -997,6 +998,11 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, struct bNodeSocke void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); +void ntreeCompositCryptomatteSyncFromAdd(bNodeTree *ntree, bNode *node); +void ntreeCompositCryptomatteSyncFromRemove(bNodeTree *ntree, bNode *node); +struct bNodeSocket *ntreeCompositCryptomatteAddSocket(struct bNodeTree *ntree, struct bNode *node); +int ntreeCompositCryptomatteRemoveSocket(struct bNodeTree *ntree, struct bNode *node); + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 499f92b3878..f15d90100d2 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3528,6 +3528,7 @@ static void registerCompositNodes(void) register_node_type_cmp_doubleedgemask(); register_node_type_cmp_keyingscreen(); register_node_type_cmp_keying(); + register_node_type_cmp_cryptomatte(); register_node_type_cmp_translate(); register_node_type_cmp_rotate(); diff --git a/source/blender/blenlib/BLI_hash_mm3.h b/source/blender/blenlib/BLI_hash_mm3.h new file mode 100644 index 00000000000..93bf963c9a4 --- /dev/null +++ b/source/blender/blenlib/BLI_hash_mm3.h @@ -0,0 +1,40 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 + * of the License, or (at your option) any later version. + * + * 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BLI_HASH_MM3_H__ +#define __BLI_HASH_MM3_H__ + +/** \file BLI_hash_mm3.h + * \ingroup bli + */ + +#include "BLI_sys_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t BLI_hash_mm3(const unsigned char *data, size_t len, uint32_t seed); + +#ifdef __cplusplus +} +#endif + +#endif /* __BLI_HASH_MM2A_H__ */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 16497c12022..e3f5773b1e4 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -74,6 +74,7 @@ set(SRC intern/gsqueue.c intern/hash_md5.c intern/hash_mm2a.c + intern/hash_mm3.c intern/jitter_2d.c intern/lasso_2d.c intern/list_sort_impl.h @@ -159,6 +160,7 @@ set(SRC BLI_hash.h BLI_hash_md5.h BLI_hash_mm2a.h + BLI_hash_mm3.h BLI_heap.h BLI_jitter_2d.h BLI_kdopbvh.h diff --git a/source/blender/blenlib/intern/hash_mm3.c b/source/blender/blenlib/intern/hash_mm3.c new file mode 100644 index 00000000000..ac483795e45 --- /dev/null +++ b/source/blender/blenlib/intern/hash_mm3.c @@ -0,0 +1,147 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 + * of the License, or (at your option) any later version. + * + * 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. + * + * ***** END GPL LICENSE BLOCK ***** + * + * Copyright (C) 2018 Blender Foundation. + * + */ + +/** \file blender/blenlib/intern/hash_mm3.c + * \ingroup bli + * + * Functions to compute Murmur3 hash key. + * + * This Code is based on alShaders/Cryptomatte/MurmurHash3.h: + * + * MurmurHash3 was written by Austin Appleby, and is placed in the public + * domain. The author hereby disclaims copyright to this source code. + * + */ + +#include "BLI_compiler_compat.h" +#include "BLI_compiler_attrs.h" +#include "BLI_hash_mm3.h" /* own include */ + +#if defined(_MSC_VER) +# include +# define ROTL32(x,y) _rotl(x,y) +# define BIG_CONSTANT(x) (x) + +/* Other compilers */ +#else /* defined(_MSC_VER) */ +static inline uint32_t rotl32(uint32_t x, int8_t r) +{ + return (x << r) | (x >> (32 - r)); +} +# define ROTL32(x,y) rotl32(x,y) +# define BIG_CONSTANT(x) (x##LLU) +#endif /* !defined(_MSC_VER) */ + +/* Block read - if your platform needs to do endian-swapping or can only + * handle aligned reads, do the conversion here + */ + +BLI_INLINE uint32_t getblock32(const uint32_t * p, int i) +{ + return p[i]; +} + +BLI_INLINE uint64_t getblock64(const uint64_t * p, int i) +{ + return p[i]; +} + +/* Finalization mix - force all bits of a hash block to avalanche */ + +BLI_INLINE uint32_t fmix32(uint32_t h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +BLI_INLINE uint64_t fmix64(uint64_t k) +{ + k ^= k >> 33; + k *= BIG_CONSTANT(0xff51afd7ed558ccd); + k ^= k >> 33; + k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); + k ^= k >> 33; + + return k; +} + +uint32_t BLI_hash_mm3(const unsigned char *in, size_t len, uint32_t seed) +{ + const uint8_t *data = (const uint8_t*)in; + const int nblocks = len / 4; + + uint32_t h1 = seed; + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + /* body */ + + const uint32_t *blocks = (const uint32_t *)(data + nblocks*4); + + for (int i = -nblocks; i; i++) { + uint32_t k1 = getblock32(blocks,i); + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + /* tail */ + + const uint8_t *tail = (const uint8_t*)(data + nblocks*4); + + uint32_t k1 = 0; + + switch (len & 3) { + case 3: + k1 ^= tail[2] << 16; + ATTR_FALLTHROUGH; + case 2: + k1 ^= tail[1] << 8; + ATTR_FALLTHROUGH; + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + h1 ^= k1; + }; + + /* finalization */ + + h1 ^= len; + + h1 = fmix32(h1); + + return h1; +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 4e845b9a60d..1def462b1ca 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3161,6 +3161,10 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree) direct_link_curvemapping(fd, node->storage); else if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) ((ImageUser *)node->storage)->ok = 1; + else if (node->type==CMP_NODE_CRYPTOMATTE) { + NodeCryptomatte *nc = (NodeCryptomatte *) node->storage; + nc->matte_id = newdataadr(fd, nc->matte_id); + } } else if ( ntree->type==NTREE_TEXTURE) { if (node->type==TEX_NODE_CURVE_RGB || node->type==TEX_NODE_CURVE_TIME) diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index e3a901f4211..4b64d0a3d3f 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1077,6 +1077,13 @@ static void write_nodetree_nolib(WriteData *wd, bNodeTree *ntree) } writestruct_id(wd, DATA, node->typeinfo->storagename, 1, node->storage); } + else if ((ntree->type == NTREE_COMPOSIT) && (node->type == CMP_NODE_CRYPTOMATTE)) { + NodeCryptomatte *nc = (NodeCryptomatte *)node->storage; + if (nc->matte_id) { + writedata(wd, DATA, strlen(nc->matte_id) + 1, nc->matte_id); + } + writestruct_id(wd, DATA, node->typeinfo->storagename, 1, node->storage); + } else { writestruct_id(wd, DATA, node->typeinfo->storagename, 1, node->storage); } diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 3e1dd83112a..0ad53d3ab80 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -182,6 +182,11 @@ set(SRC operations/COM_SunBeamsOperation.cpp operations/COM_SunBeamsOperation.h + nodes/COM_CryptomatteNode.cpp + nodes/COM_CryptomatteNode.h + operations/COM_CryptomatteOperation.cpp + operations/COM_CryptomatteOperation.h + nodes/COM_CornerPinNode.cpp nodes/COM_CornerPinNode.h nodes/COM_PlaneTrackDeformNode.cpp diff --git a/source/blender/compositor/intern/COM_Converter.cpp b/source/blender/compositor/intern/COM_Converter.cpp index 58e0da04e5e..c9181905908 100644 --- a/source/blender/compositor/intern/COM_Converter.cpp +++ b/source/blender/compositor/intern/COM_Converter.cpp @@ -55,6 +55,7 @@ extern "C" { #include "COM_Converter.h" #include "COM_CornerPinNode.h" #include "COM_CropNode.h" +#include "COM_CryptomatteNode.h" #include "COM_DefocusNode.h" #include "COM_DespeckleNode.h" #include "COM_DifferenceMatteNode.h" @@ -406,6 +407,9 @@ Node *Converter::convert(bNode *b_node) case CMP_NODE_SUNBEAMS: node = new SunBeamsNode(b_node); break; + case CMP_NODE_CRYPTOMATTE: + node = new CryptomatteNode(b_node); + break; } return node; } diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cpp b/source/blender/compositor/nodes/COM_CryptomatteNode.cpp new file mode 100644 index 00000000000..c8134068543 --- /dev/null +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cpp @@ -0,0 +1,120 @@ +/* + * Copyright 2018, Blender Foundation. + * + * 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 + * of the License, or (at your option) any later version. + * + * 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. + * + * Contributor: + * Lukas Stockner + * Stefan Werner + */ + +#include "COM_CryptomatteNode.h" +#include "COM_CryptomatteOperation.h" +#include "COM_SetAlphaOperation.h" +#include "COM_ConvertOperation.h" +#include "BLI_string.h" +#include "BLI_hash_mm3.h" +#include "BLI_assert.h" +#include + +CryptomatteNode::CryptomatteNode(bNode *editorNode) : Node(editorNode) +{ + /* pass */ +} + +/* This is taken from the Cryptomatte specification 1.0. */ +static inline float hash_to_float(uint32_t hash) { + uint32_t mantissa = hash & (( 1 << 23) - 1); + uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); + exponent = max(exponent, (uint32_t) 1); + exponent = min(exponent, (uint32_t) 254); + exponent = exponent << 23; + uint32_t sign = (hash >> 31); + sign = sign << 31; + uint32_t float_bits = sign | exponent | mantissa; + float f; + /* Bit casting relies on equal size for both types. */ + BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float and uint32_t are not the same size") + ::memcpy(&f, &float_bits, sizeof(float)); + return f; +} + +void CryptomatteNode::convertToOperations(NodeConverter &converter, const CompositorContext &/*context*/) const +{ + NodeInput *inputSocketImage = this->getInputSocket(0); + NodeOutput *outputSocketImage = this->getOutputSocket(0); + NodeOutput *outputSocketMatte = this->getOutputSocket(1); + NodeOutput *outputSocketPick = this->getOutputSocket(2); + + bNode *node = this->getbNode(); + NodeCryptomatte *cryptoMatteSettings = (NodeCryptomatte *)node->storage; + + CryptomatteOperation *operation = new CryptomatteOperation(getNumberOfInputSockets()-1); + if (cryptoMatteSettings) { + if (cryptoMatteSettings->matte_id) { + /* Split the string by commas, ignoring white space. */ + std::string input = cryptoMatteSettings->matte_id; + std::istringstream ss(input); + while (ss.good()) { + std::string token; + getline(ss, token, ','); + /* Ignore empty tokens. */ + if (token.length() > 0) { + size_t first = token.find_first_not_of(' '); + size_t last = token.find_last_not_of(' '); + if (first == std::string::npos || last == std::string::npos) { + break; + } + token = token.substr(first, (last - first + 1)); + if (*token.begin() == '<' && *(--token.end()) == '>') { + operation->addObjectIndex(atof(token.substr(1, token.length() - 2).c_str())); + } + else { + uint32_t hash = BLI_hash_mm3((const unsigned char*)token.c_str(), token.length(), 0); + operation->addObjectIndex(hash_to_float(hash)); + } + } + } + } + } + + converter.addOperation(operation); + + for (int i = 0; i < getNumberOfInputSockets()-1; ++i) { + converter.mapInputSocket(this->getInputSocket(i + 1), operation->getInputSocket(i)); + } + + SeparateChannelOperation *separateOperation = new SeparateChannelOperation; + separateOperation->setChannel(3); + converter.addOperation(separateOperation); + + SetAlphaOperation *operationAlpha = new SetAlphaOperation(); + converter.addOperation(operationAlpha); + + converter.addLink(operation->getOutputSocket(0), separateOperation->getInputSocket(0)); + converter.addLink(separateOperation->getOutputSocket(0), operationAlpha->getInputSocket(1)); + + SetAlphaOperation *clearAlphaOperation = new SetAlphaOperation(); + converter.addOperation(clearAlphaOperation); + converter.addInputValue(clearAlphaOperation->getInputSocket(1), 1.0f); + + converter.addLink(operation->getOutputSocket(0), clearAlphaOperation->getInputSocket(0)); + + converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0)); + converter.mapOutputSocket(outputSocketMatte, separateOperation->getOutputSocket(0)); + converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket(0)); + converter.mapOutputSocket(outputSocketPick, clearAlphaOperation->getOutputSocket(0)); + +} diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.h b/source/blender/compositor/nodes/COM_CryptomatteNode.h new file mode 100644 index 00000000000..5251b57d8af --- /dev/null +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.h @@ -0,0 +1,38 @@ +/* + * Copyright 2018, Blender Foundation. + * + * 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 + * of the License, or (at your option) any later version. + * + * 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. + * + * Contributor: + * Lukas Stockner + */ + +#ifndef _COM_CryptomatteNode_h_ +#define _COM_CryptomatteNode_h_ + +#include "COM_Node.h" + +/** + * @brief CryptomatteNode + * @ingroup Node + */ +class CryptomatteNode : public Node { +public: + CryptomatteNode(bNode *editorNode); + void convertToOperations(NodeConverter &converter, const CompositorContext &context) const; +}; + +#endif + diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cpp b/source/blender/compositor/operations/COM_CryptomatteOperation.cpp new file mode 100644 index 00000000000..9dd36863d37 --- /dev/null +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2018, Blender Foundation. + * + * 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 + * of the License, or (at your option) any later version. + * + * 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. + * + * Contributor: Lukas Stockner, Stefan Werner + */ + +#include "COM_CryptomatteOperation.h" + +CryptomatteOperation::CryptomatteOperation(size_t num_inputs) : NodeOperation() +{ + for(size_t i = 0; i < num_inputs; i++) { + this->addInputSocket(COM_DT_COLOR); + } + inputs.resize(num_inputs); + this->addOutputSocket(COM_DT_COLOR); + this->setComplex(true); +} + +void CryptomatteOperation::initExecution() +{ + for (size_t i = 0; i < inputs.size(); i++) { + inputs[i] = this->getInputSocketReader(i); + } +} + +void CryptomatteOperation::addObjectIndex(float objectIndex) +{ + if (objectIndex != 0.0f) { + m_objectIndex.push_back(objectIndex); + } +} + +void CryptomatteOperation::executePixel(float output[4], + int x, + int y, + void *data) +{ + float input[4]; + output[0] = output[1] = output[2] = output[3] = 0.0f; + for (size_t i = 0; i < inputs.size(); i++) { + inputs[i]->read(input, x, y, data); + if (i == 0) { + /* Write the frontmost object as false color for picking. */ + output[0] = input[0]; + uint32_t m3hash; + ::memcpy(&m3hash, &input[0], sizeof(uint32_t)); + /* Since the red channel is likely to be out of display range, + * setting green and blue gives more meaningful images. */ + output[1] = ((float) ((m3hash << 8)) / (float) UINT32_MAX); + output[2] = ((float) ((m3hash << 16)) / (float) UINT32_MAX); + } + for(size_t i = 0; i < m_objectIndex.size(); i++) { + if (m_objectIndex[i] == input[0]) { + output[3] += input[1]; + } + if (m_objectIndex[i] == input[2]) { + output[3] += input[3]; + } + } + } +} diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.h b/source/blender/compositor/operations/COM_CryptomatteOperation.h new file mode 100644 index 00000000000..9ce02c048b3 --- /dev/null +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.h @@ -0,0 +1,40 @@ +/* + * Copyright 2018, Blender Foundation. + * + * 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 + * of the License, or (at your option) any later version. + * + * 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. + * + * Contributor: Lukas Stockner, Stefan Werner + */ + +#ifndef _COM_CryptomatteOperation_h +#define _COM_CryptomatteOperation_h +#include "COM_NodeOperation.h" + + +class CryptomatteOperation : public NodeOperation { +private: + std::vector m_objectIndex; +public: + std::vector inputs; + + CryptomatteOperation(size_t num_inputs = 6); + + void initExecution(); + void executePixel(float output[4], int x, int y, void *data); + + void addObjectIndex(float objectIndex); + +}; +#endif diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 51f79e05bf9..eefbeee6336 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -969,6 +969,7 @@ void uiTemplateCurveMapping( bool levels, bool brush, bool neg_slope); void uiTemplateColorPicker(uiLayout *layout, struct PointerRNA *ptr, const char *propname, bool value_slider, bool lock, bool lock_luminosity, bool cubic); void uiTemplatePalette(uiLayout *layout, struct PointerRNA *ptr, const char *propname, bool color); +void uiTemplateCryptoPicker(uiLayout *layout, struct PointerRNA *ptr, const char *propname); void uiTemplateLayers( uiLayout *layout, struct PointerRNA *ptr, const char *propname, PointerRNA *used_ptr, const char *used_propname, int active_layer); diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index fafe5f48f75..67486f38760 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -82,6 +82,7 @@ wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf) /* assign to operators */ WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorband"); WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color"); + WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color_crypto"); WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_id"); WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth"); WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver"); diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 7dd7de0817e..bcb60013eda 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -36,6 +36,7 @@ #include "DNA_screen_types.h" #include "BLI_math_vector.h" +#include "BLI_string.h" #include "BKE_context.h" #include "BKE_main.h" @@ -72,6 +73,8 @@ typedef struct Eyedropper { bool accum_start; /* has mouse been pressed */ float accum_col[3]; int accum_tot; + + bool accumulate; /* Color picking for cryptomatte, without accumulation. */ } Eyedropper; static bool eyedropper_init(bContext *C, wmOperator *op) @@ -80,6 +83,7 @@ static bool eyedropper_init(bContext *C, wmOperator *op) Eyedropper *eye; op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper"); + eye->accumulate = !STREQ(op->type->idname, "UI_OT_eyedropper_color_crypto"); UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index); @@ -207,29 +211,30 @@ static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3 RNA_property_update(C, &eye->ptr, eye->prop); } -/* set sample from accumulated values */ -static void eyedropper_color_set_accum(bContext *C, Eyedropper *eye) -{ - float col[3]; - mul_v3_v3fl(col, eye->accum_col, 1.0f / (float)eye->accum_tot); - eyedropper_color_set(C, eye, col); -} - -/* single point sample & set */ static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my) { + /* Accumulate color. */ float col[3]; eyedropper_color_sample_fl(C, mx, my, col); - eyedropper_color_set(C, eye, col); -} -static void eyedropper_color_sample_accum(bContext *C, Eyedropper *eye, int mx, int my) -{ - float col[3]; - eyedropper_color_sample_fl(C, mx, my, col); - /* delay linear conversion */ - add_v3_v3(eye->accum_col, col); - eye->accum_tot++; + if (eye->accumulate) { + add_v3_v3(eye->accum_col, col); + eye->accum_tot++; + } + else { + copy_v3_v3(eye->accum_col, col); + eye->accum_tot = 1; + } + + /* Apply to property. */ + float accum_col[3]; + if (eye->accum_tot > 1) { + mul_v3_v3fl(accum_col, eye->accum_col, 1.0f / (float)eye->accum_tot); + } + else { + copy_v3_v3(accum_col, eye->accum_col); + } + eyedropper_color_set(C, eye, accum_col); } static void eyedropper_cancel(bContext *C, wmOperator *op) @@ -254,29 +259,24 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) if (eye->accum_tot == 0) { eyedropper_color_sample(C, eye, event->x, event->y); } - else { - eyedropper_color_set_accum(C, eye); - } eyedropper_exit(C, op); return OPERATOR_FINISHED; case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ eye->accum_start = true; - eyedropper_color_sample_accum(C, eye, event->x, event->y); + eyedropper_color_sample(C, eye, event->x, event->y); break; case EYE_MODAL_SAMPLE_RESET: eye->accum_tot = 0; zero_v3(eye->accum_col); - eyedropper_color_sample_accum(C, eye, event->x, event->y); - eyedropper_color_set_accum(C, eye); + eyedropper_color_sample(C, eye, event->x, event->y); break; } } - else if (event->type == MOUSEMOVE) { + else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { if (eye->accum_start) { /* button is pressed so keep sampling */ - eyedropper_color_sample_accum(C, eye, event->x, event->y); - eyedropper_color_set_accum(C, eye); + eyedropper_color_sample(C, eye, event->x, event->y); } } @@ -321,20 +321,9 @@ static int eyedropper_exec(bContext *C, wmOperator *op) static bool eyedropper_poll(bContext *C) { - PointerRNA ptr; - PropertyRNA *prop; - int index_dummy; - uiBut *but; - - /* Only color buttons */ - if ((CTX_wm_window(C) != NULL) && - (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) && - (but->type == UI_BTYPE_COLOR)) - { - return 1; - } - - return 0; + /* Actual test for active button happens later, since we don't + * know which one is active until mouse over. */ + return (CTX_wm_window(C) != NULL); } void UI_OT_eyedropper_color(wmOperatorType *ot) @@ -353,6 +342,22 @@ void UI_OT_eyedropper_color(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL; +} + +void UI_OT_eyedropper_color_crypto(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Cryptomatte Eyedropper"; + ot->idname = "UI_OT_eyedropper_color_crypto"; + ot->description = "Pick a color from Cryptomatte node Pick output image"; - /* properties */ + /* api callbacks */ + ot->invoke = eyedropper_invoke; + ot->modal = eyedropper_modal; + ot->cancel = eyedropper_cancel; + ot->exec = eyedropper_exec; + ot->poll = eyedropper_poll; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL; } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index cc86530871d..dc5e100b5f2 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -763,6 +763,7 @@ struct wmKeyMap *eyedropper_colorband_modal_keymap(struct wmKeyConfig *keyconf); /* interface_eyedropper_color.c */ void UI_OT_eyedropper_color(struct wmOperatorType *ot); +void UI_OT_eyedropper_color_crypto(struct wmOperatorType *ot); /* interface_eyedropper_colorband.c */ void UI_OT_eyedropper_colorband(struct wmOperatorType *ot); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 9a4ea41141a..1e67ecdfc90 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1138,6 +1138,7 @@ void ED_operatortypes_ui(void) /* external */ WM_operatortype_append(UI_OT_eyedropper_color); + WM_operatortype_append(UI_OT_eyedropper_color_crypto); WM_operatortype_append(UI_OT_eyedropper_colorband); WM_operatortype_append(UI_OT_eyedropper_colorband_point); WM_operatortype_append(UI_OT_eyedropper_id); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index bd4bb032717..daee0d3af3f 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2572,6 +2572,24 @@ void uiTemplatePalette(uiLayout *layout, PointerRNA *ptr, const char *propname, } } +void uiTemplateCryptoPicker(uiLayout *layout, PointerRNA *ptr, const char *propname) +{ + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); + uiBlock *block; + uiBut *but; + + if (!prop) { + RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname); + return; + } + + block = uiLayoutGetBlock(layout); + + but = uiDefIconTextButO(block, UI_BTYPE_BUT, "UI_OT_eyedropper_color_crypto", WM_OP_INVOKE_DEFAULT, ICON_EYEDROPPER, RNA_property_ui_name(prop), 0, 0, UI_UNIT_X, UI_UNIT_Y, RNA_property_ui_description(prop)); + but->rnapoin = *ptr; + but->rnaprop = prop; + but->rnaindex = -1; +} /********************* Layer Buttons Template ************************/ diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index b0440b39823..23df1b72c37 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -2544,6 +2544,25 @@ static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), P uiItemR(layout, ptr, "ray_length", UI_ITEM_R_SLIDER, NULL, ICON_NONE); } +static void node_composit_buts_cryptomatte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col = uiLayoutColumn(layout, true); + + uiItemL(col, IFACE_("Matte Objects:"), ICON_NONE); + + uiLayout *row = uiLayoutRow(col, true); + uiTemplateCryptoPicker(row, ptr, "add"); + uiTemplateCryptoPicker(row, ptr, "remove"); + + uiItemR(col, ptr, "matte_id", 0, "", ICON_NONE); +} + +static void node_composit_buts_cryptomatte_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *UNUSED(ptr)) +{ + uiItemO(layout, IFACE_("Add Crypto Layer"), ICON_ZOOMIN, "NODE_OT_cryptomatte_layer_add"); + uiItemO(layout, IFACE_("Remove Crypto Layer"), ICON_ZOOMOUT, "NODE_OT_cryptomatte_layer_remove"); +} + static void node_composit_buts_brightcontrast(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "use_premultiply", 0, NULL, ICON_NONE); @@ -2776,6 +2795,10 @@ static void node_composit_set_butfunc(bNodeType *ntype) case CMP_NODE_SUNBEAMS: ntype->draw_buttons = node_composit_buts_sunbeams; break; + case CMP_NODE_CRYPTOMATTE: + ntype->draw_buttons = node_composit_buts_cryptomatte; + ntype->draw_buttons_ex = node_composit_buts_cryptomatte_ex; + break; case CMP_NODE_BRIGHTCONTRAST: ntype->draw_buttons = node_composit_buts_brightcontrast; } diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index c49c8c201c4..97c5157486d 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -2611,3 +2611,93 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/* ****************** Cryptomatte Add Socket ******************* */ + +static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + PointerRNA ptr = CTX_data_pointer_get(C, "node"); + bNodeTree *ntree = NULL; + bNode *node = NULL; + + if (ptr.data) { + node = ptr.data; + ntree = ptr.id.data; + } + else if (snode && snode->edittree) { + ntree = snode->edittree; + node = nodeGetActive(snode->edittree); + } + + if (!node || node->type != CMP_NODE_CRYPTOMATTE) { + return OPERATOR_CANCELLED; + } + + ntreeCompositCryptomatteAddSocket(ntree, node); + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Cryptomatte Socket"; + ot->description = "Add a new input layer to a Cryptomatte node"; + ot->idname = "NODE_OT_cryptomatte_layer_add"; + + /* callbacks */ + ot->exec = node_cryptomatte_add_socket_exec; + ot->poll = composite_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ****************** Cryptomatte Remove Socket ******************* */ + +static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + PointerRNA ptr = CTX_data_pointer_get(C, "node"); + bNodeTree *ntree = NULL; + bNode *node = NULL; + + if (ptr.data) { + node = ptr.data; + ntree = ptr.id.data; + } + else if (snode && snode->edittree) { + ntree = snode->edittree; + node = nodeGetActive(snode->edittree); + } + + if (!node || node->type != CMP_NODE_CRYPTOMATTE) { + return OPERATOR_CANCELLED; + } + + if (!ntreeCompositCryptomatteRemoveSocket(ntree, node)) { + return OPERATOR_CANCELLED; + } + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Cryptomatte Socket"; + ot->description = "Remove layer from a Crytpomatte node"; + ot->idname = "NODE_OT_cryptomatte_layer_remove"; + + /* callbacks */ + ot->exec = node_cryptomatte_remove_socket_exec; + ot->poll = composite_node_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index fd62c52bd5a..9b396aa9642 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -222,6 +222,9 @@ void NODE_OT_shader_script_update(struct wmOperatorType *ot); void NODE_OT_viewer_border(struct wmOperatorType *ot); void NODE_OT_clear_viewer_border(struct wmOperatorType *ot); +void NODE_OT_cryptomatte_layer_add(struct wmOperatorType *ot); +void NODE_OT_cryptomatte_layer_remove(struct wmOperatorType *ot); + extern const char *node_context_dir[]; // XXXXXX diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index d7ded62a988..ea07341beb9 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -130,6 +130,9 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_tree_socket_add); WM_operatortype_append(NODE_OT_tree_socket_remove); WM_operatortype_append(NODE_OT_tree_socket_move); + + WM_operatortype_append(NODE_OT_cryptomatte_layer_add); + WM_operatortype_append(NODE_OT_cryptomatte_layer_remove); } void ED_operatormacros_node(void) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 129172315dd..00758bd1379 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -907,6 +907,14 @@ typedef struct NodeSunBeams { float ray_length; } NodeSunBeams; +typedef struct NodeCryptomatte { + float add[3]; + float remove[3]; + char *matte_id; + int num_inputs; + int pad; +} NodeCryptomatte; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index e66c1e937e6..52fbadd0f54 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2880,6 +2880,48 @@ static void rna_NodeColorBalance_update_cdl(Main *bmain, Scene *scene, PointerRN rna_Node_update(bmain, scene, ptr); } +static void rna_NodeCryptomatte_matte_get(PointerRNA *ptr, char *value) +{ + bNode *node = (bNode *)ptr->data; + NodeCryptomatte *nc = node->storage; + + strcpy(value, (nc->matte_id) ? nc->matte_id : ""); +} + +static int rna_NodeCryptomatte_matte_length(PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + NodeCryptomatte *nc = node->storage; + + return (nc->matte_id) ? strlen(nc->matte_id) : 0; +} + +static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value) +{ + bNode *node = (bNode *)ptr->data; + NodeCryptomatte *nc = node->storage; + + if (nc->matte_id) + MEM_freeN(nc->matte_id); + + if (value && value[0]) + nc->matte_id = BLI_strdup(value); + else + nc->matte_id = NULL; +} + +static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + ntreeCompositCryptomatteSyncFromAdd(ptr->id.data, ptr->data); + rna_Node_update(bmain, scene, ptr); +} + +static void rna_NodeCryptomatte_update_remove(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + ntreeCompositCryptomatteSyncFromRemove(ptr->id.data, ptr->data); + rna_Node_update(bmain, scene, ptr); +} + /* ******** Node Socket Types ******** */ static PointerRNA rna_NodeOutputFile_slot_layer_get(CollectionPropertyIterator *iter) @@ -6977,6 +7019,32 @@ static void def_cmp_sunbeams(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_cmp_cryptomatte(StructRNA *srna) +{ + PropertyRNA *prop; + static float default_1[3] = {1.f, 1.f, 1.f}; + + RNA_def_struct_sdna_from(srna, "NodeCryptomatte", "storage"); + prop = RNA_def_property(srna, "matte_id", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, "rna_NodeCryptomatte_matte_get", "rna_NodeCryptomatte_matte_length", + "rna_NodeCryptomatte_matte_set"); + RNA_def_property_ui_text(prop, "Matte Objects", "List of object and material crypto IDs to include in matte"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + + prop = RNA_def_property(srna, "add", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_array_default(prop, default_1); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_text(prop, "Add", "Add object or material to matte, by picking a color from the Pick output"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeCryptomatte_update_add"); + + prop = RNA_def_property(srna, "remove", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_array_default(prop, default_1); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_text(prop, "Remove", "Remove object or material from matte, by picking a color from the Pick output"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeCryptomatte_update_remove"); +} + /* -- Texture Nodes --------------------------------------------------------- */ static void def_tex_output(StructRNA *srna) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index cc0bef30047..5a5d5997878 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC composite/nodes/node_composite_composite.c composite/nodes/node_composite_cornerpin.c composite/nodes/node_composite_crop.c + composite/nodes/node_composite_cryptomatte.c composite/nodes/node_composite_curves.c composite/nodes/node_composite_despeckle.c composite/nodes/node_composite_doubleEdgeMask.c diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index a5c2e604f46..2a82b706de5 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -109,6 +109,7 @@ void register_node_type_cmp_luma_matte(void); void register_node_type_cmp_doubleedgemask(void); void register_node_type_cmp_keyingscreen(void); void register_node_type_cmp_keying(void); +void register_node_type_cmp_cryptomatte(void); void register_node_type_cmp_translate(void); void register_node_type_cmp_rotate(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 5217c7dc6e7..a4f8a798576 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -219,6 +219,7 @@ DefNode( CompositorNode, CMP_NODE_PIXELATE, 0, "PIXEL DefNode( CompositorNode, CMP_NODE_PLANETRACKDEFORM,def_cmp_planetrackdeform,"PLANETRACKDEFORM",PlaneTrackDeform,"Plane Track Deform","" ) DefNode( CompositorNode, CMP_NODE_CORNERPIN, 0, "CORNERPIN", CornerPin, "Corner Pin", "" ) DefNode( CompositorNode, CMP_NODE_SUNBEAMS, def_cmp_sunbeams, "SUNBEAMS", SunBeams, "Sun Beams", "" ) +DefNode( CompositorNode, CMP_NODE_CRYPTOMATTE, def_cmp_cryptomatte, "CRYPTOMATTE", Cryptomatte, "Cryptomatte", "" ) DefNode( TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) DefNode( TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c new file mode 100644 index 00000000000..488dfa6756e --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c @@ -0,0 +1,309 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 + * of the License, or (at your option) any later version. + * + * 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) 2018 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Stockner, Stefan Werner + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/nodes/composite/nodes/node_composite_cryptomatte.c + * \ingroup cmpnodes + */ + +#include "node_composite_util.h" +#include "BLI_dynstr.h" +#include "BLI_hash_mm3.h" +#include "BLI_assert.h" + +#ifndef max + #define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min + #define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* this is taken from the cryptomatte specification 1.0 */ + +static inline float hash_to_float(uint32_t hash) +{ + uint32_t mantissa = hash & (( 1 << 23) - 1); + uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); + exponent = max(exponent, (uint32_t) 1); + exponent = min(exponent, (uint32_t) 254); + exponent = exponent << 23; + uint32_t sign = (hash >> 31); + sign = sign << 31; + uint32_t float_bits = sign | exponent | mantissa; + float f; + /* Bit casting relies on equal size for both types. */ + BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float and uint32_t are not the same size") + memcpy(&f, &float_bits, sizeof(float)); + return f; +} + +static void cryptomatte_add(NodeCryptomatte* n, float f) +{ + /* Turn the number into a string. */ + char number[32]; + BLI_snprintf(number, sizeof(number), "<%.9g>", f); + + /* Search if we already have the number. */ + if (n->matte_id && strlen(n->matte_id) != 0) { + size_t start = 0; + const size_t end = strlen(n->matte_id); + size_t token_len = 0; + while (start < end) { + /* Ignore leading whitespace. */ + while (start < end && n->matte_id[start] == ' ') { + ++start; + } + + /* Find the next seprator. */ + char* token_end = strchr(n->matte_id+start, ','); + if (token_end == NULL || token_end == n->matte_id+start) { + token_end = n->matte_id+end; + } + /* Be aware that token_len still contains any trailing white space. */ + token_len = token_end - (n->matte_id + start); + + /* If this has a leading bracket, assume a raw floating point number and look for the closing bracket. */ + if (n->matte_id[start] == '<') { + if (strncmp(n->matte_id+start, number, strlen(number)) == 0) { + /* This number is already there, so continue. */ + return; + } + } + else { + /* Remove trailing white space */ + size_t name_len = token_len; + while (n->matte_id[start+name_len] == ' ' && name_len > 0) { + name_len--; + } + /* Calculate the hash of the token and compare. */ + uint32_t hash = BLI_hash_mm3((const unsigned char*)(n->matte_id+start), name_len, 0); + if (f == hash_to_float(hash)) { + return; + } + } + start += token_len+1; + } + } + + DynStr *new_matte = BLI_dynstr_new(); + if (!new_matte) { + return; + } + + if(n->matte_id) { + BLI_dynstr_append(new_matte, n->matte_id); + MEM_freeN(n->matte_id); + } + + if(BLI_dynstr_get_len(new_matte) > 0) { + BLI_dynstr_append(new_matte, ","); + } + BLI_dynstr_append(new_matte, number); + n->matte_id = BLI_dynstr_get_cstring(new_matte); + BLI_dynstr_free(new_matte); +} + +static void cryptomatte_remove(NodeCryptomatte*n, float f) +{ + if (n->matte_id == NULL || strlen(n->matte_id) == 0) { + /* Empty string, nothing to remove. */ + return; + } + + /* This will be the new string without the removed key. */ + DynStr *new_matte = BLI_dynstr_new(); + if (!new_matte) { + return; + } + + /* Turn the number into a string. */ + static char number[32]; + BLI_snprintf(number, sizeof(number), "<%.9g>", f); + + /* Search if we already have the number. */ + size_t start = 0; + const size_t end = strlen(n->matte_id); + size_t token_len = 0; + bool is_first = true; + while (start < end) { + bool skip = false; + /* Ignore leading whitespace or commas. */ + while (start < end && ((n->matte_id[start] == ' ') || (n->matte_id[start] == ','))) { + ++start; + } + + /* Find the next seprator. */ + char* token_end = strchr(n->matte_id+start+1, ','); + if (token_end == NULL || token_end == n->matte_id+start) { + token_end = n->matte_id+end; + } + /* Be aware that token_len still contains any trailing white space. */ + token_len = token_end - (n->matte_id + start); + + if (token_len == 1) { + skip = true; + } + /* If this has a leading bracket, assume a raw floating point number and look for the closing bracket. */ + else if (n->matte_id[start] == '<') { + if (strncmp(n->matte_id+start, number, strlen(number)) == 0) { + /* This number is already there, so skip it. */ + skip = true; + } + } + else { + /* Remove trailing white space */ + size_t name_len = token_len; + while (n->matte_id[start+name_len] == ' ' && name_len > 0) { + name_len--; + } + /* Calculate the hash of the token and compare. */ + uint32_t hash = BLI_hash_mm3((const unsigned char*)(n->matte_id+start), name_len, 0); + if (f == hash_to_float(hash)) { + skip = true; + } + } + if (!skip) { + if (is_first) { + is_first = false; + } + else { + BLI_dynstr_append(new_matte, ", "); + } + BLI_dynstr_nappend(new_matte, n->matte_id+start, token_len); + } + start += token_len+1; + } + + if(n->matte_id) { + MEM_freeN(n->matte_id); + n->matte_id = NULL; + } + if(BLI_dynstr_get_len(new_matte) > 0) { + n->matte_id = BLI_dynstr_get_cstring(new_matte); + } + BLI_dynstr_free(new_matte); +} + +static bNodeSocketTemplate outputs[] = { + { SOCK_RGBA, 0, N_("Image")}, + { SOCK_FLOAT, 0, N_("Matte")}, + { SOCK_RGBA, 0, N_("Pick")}, + { -1, 0, "" } +}; + +void ntreeCompositCryptomatteSyncFromAdd(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeCryptomatte *n = node->storage; + if (n->add[0] != 0.0f) { + cryptomatte_add(n, n->add[0]); + n->add[0] = 0.0f; + n->add[1] = 0.0f; + n->add[2] = 0.0f; + } +} + +void ntreeCompositCryptomatteSyncFromRemove(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeCryptomatte *n = node->storage; + if (n->remove[0] != 0.0f) { + cryptomatte_remove(n, n->remove[0]); + n->remove[0] = 0.0f; + n->remove[1] = 0.0f; + n->remove[2] = 0.0f; + } +} + +bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node) +{ + NodeCryptomatte *n = node->storage; + char sockname[32]; + n->num_inputs++; + BLI_snprintf(sockname, sizeof(sockname), "Crypto %.2d", n->num_inputs-1); + bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, sockname); + return sock; +} + +int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node) +{ + NodeCryptomatte *n = node->storage; + if (n->num_inputs < 2) { + return 0; + } + bNodeSocket *sock = node->inputs.last; + nodeRemoveSocket(ntree, node, sock); + n->num_inputs--; + return 1; +} + +static void init(bNodeTree *ntree, bNode *node) +{ + NodeCryptomatte *user = MEM_callocN(sizeof(NodeCryptomatte), "cryptomatte user"); + node->storage = user; + + + nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, "image", "Image"); + + /* Add three inputs by default, as recommended by the Cryptomatte specification. */ + ntreeCompositCryptomatteAddSocket(ntree, node); + ntreeCompositCryptomatteAddSocket(ntree, node); + ntreeCompositCryptomatteAddSocket(ntree, node); +} + +static void node_free_cryptomatte(bNode *node) +{ + NodeCryptomatte *nc = node->storage; + + if (nc) { + if (nc->matte_id) { + MEM_freeN(nc->matte_id); + } + + MEM_freeN(nc); + } +} + +static void node_copy_cryptomatte(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, bNode *src_node) +{ + NodeCryptomatte *src_nc = src_node->storage; + NodeCryptomatte *dest_nc = MEM_dupallocN(src_nc); + + if (src_nc->matte_id) + dest_nc->matte_id = MEM_dupallocN(src_nc->matte_id); + + dest_node->storage = dest_nc; +} + +void register_node_type_cmp_cryptomatte(void) +{ + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE, "Cryptomatte", NODE_CLASS_CONVERTOR, 0); + node_type_socket_templates(&ntype, NULL, outputs); + node_type_init(&ntype, init); + node_type_storage(&ntype, "NodeCryptomatte", node_free_cryptomatte, node_copy_cryptomatte); + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From 566b319335563888e252b2186c93606ad41ff216 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 18 Jul 2018 13:34:22 +0200 Subject: Cleanup: Cryptomatte node style tweaks. --- source/blender/blenlib/intern/hash_mm3.c | 2 +- source/blender/compositor/nodes/COM_CryptomatteNode.cpp | 5 +++-- .../nodes/composite/nodes/node_composite_cryptomatte.c | 17 +++++------------ 3 files changed, 9 insertions(+), 15 deletions(-) (limited to 'source/blender') diff --git a/source/blender/blenlib/intern/hash_mm3.c b/source/blender/blenlib/intern/hash_mm3.c index ac483795e45..5ead9ceca63 100644 --- a/source/blender/blenlib/intern/hash_mm3.c +++ b/source/blender/blenlib/intern/hash_mm3.c @@ -49,7 +49,7 @@ static inline uint32_t rotl32(uint32_t x, int8_t r) return (x << r) | (x >> (32 - r)); } # define ROTL32(x,y) rotl32(x,y) -# define BIG_CONSTANT(x) (x##LLU) +# define BIG_CONSTANT(x) (x##LLU) #endif /* !defined(_MSC_VER) */ /* Block read - if your platform needs to do endian-swapping or can only diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cpp b/source/blender/compositor/nodes/COM_CryptomatteNode.cpp index c8134068543..bc115e66c20 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cpp +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cpp @@ -35,7 +35,8 @@ CryptomatteNode::CryptomatteNode(bNode *editorNode) : Node(editorNode) } /* This is taken from the Cryptomatte specification 1.0. */ -static inline float hash_to_float(uint32_t hash) { +static inline float hash_to_float(uint32_t hash) +{ uint32_t mantissa = hash & (( 1 << 23) - 1); uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); exponent = max(exponent, (uint32_t) 1); @@ -95,7 +96,7 @@ void CryptomatteNode::convertToOperations(NodeConverter &converter, const Compos for (int i = 0; i < getNumberOfInputSockets()-1; ++i) { converter.mapInputSocket(this->getInputSocket(i + 1), operation->getInputSocket(i)); } - + SeparateChannelOperation *separateOperation = new SeparateChannelOperation; separateOperation->setChannel(3); converter.addOperation(separateOperation); diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c index 488dfa6756e..0231e4717b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c @@ -4,7 +4,7 @@ * 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 - * of the License, or (at your option) any later version. + * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -30,17 +30,10 @@ */ #include "node_composite_util.h" +#include "BLI_assert.h" #include "BLI_dynstr.h" #include "BLI_hash_mm3.h" -#include "BLI_assert.h" - -#ifndef max - #define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef min - #define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif +#include "BLI_utildefines.h" /* this is taken from the cryptomatte specification 1.0 */ @@ -48,8 +41,8 @@ static inline float hash_to_float(uint32_t hash) { uint32_t mantissa = hash & (( 1 << 23) - 1); uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); - exponent = max(exponent, (uint32_t) 1); - exponent = min(exponent, (uint32_t) 254); + exponent = MAX2(exponent, (uint32_t) 1); + exponent = MIN2(exponent, (uint32_t) 254); exponent = exponent << 23; uint32_t sign = (hash >> 31); sign = sign << 31; -- cgit v1.2.3 From 5078b9d2d08a34ae3786100c2301ea960165e7f2 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Wed, 18 Jul 2018 11:14:43 +0200 Subject: Cycles: add Principled Hair BSDF. This is a physically-based, easy-to-use shader for rendering hair and fur, with controls for melanin, roughness and randomization. Based on the paper "A Practical and Controllable Hair and Fur Model for Production Path Tracing". Implemented by Leonardo E. Segovia and Lukas Stockner, part of Google Summer of Code 2018. --- source/blender/blenkernel/BKE_node.h | 2 + source/blender/blenkernel/intern/node.c | 1 + source/blender/editors/space_node/drawnode.c | 8 ++ source/blender/makesdna/DNA_node_types.h | 5 + source/blender/makesrna/intern/rna_nodetree.c | 22 ++++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_shader.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../nodes/node_shader_bsdf_hair_principled.c | 133 +++++++++++++++++++++ 9 files changed, 174 insertions(+) create mode 100644 source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 8e54c6a87c4..23ef45416f7 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -794,6 +794,8 @@ struct ShadeResult; #define SH_NODE_DISPLACEMENT 198 #define SH_NODE_VECTOR_DISPLACEMENT 199 #define SH_NODE_VOLUME_PRINCIPLED 200 +/* 201..700 occupied by other node types, continue from 701 */ +#define SH_NODE_BSDF_HAIR_PRINCIPLED 701 /* custom defines options for Material node */ #define SH_NODE_MAT_DIFF 1 diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index f15d90100d2..c830917a547 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3622,6 +3622,7 @@ static void registerShaderNodes(void) register_node_type_sh_bsdf_velvet(); register_node_type_sh_bsdf_toon(); register_node_type_sh_bsdf_hair(); + register_node_type_sh_bsdf_hair_principled(); register_node_type_sh_emission(); register_node_type_sh_holdout(); register_node_type_sh_volume_absorption(); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 23df1b72c37..6e52af2898e 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1111,6 +1111,11 @@ static void node_shader_buts_hair(uiLayout *layout, bContext *UNUSED(C), Pointer uiItemR(layout, ptr, "component", 0, "", ICON_NONE); } +static void node_shader_buts_principled_hair(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "parametrization", 0, "", ICON_NONE); +} + static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *row; @@ -1301,6 +1306,9 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_BSDF_HAIR: ntype->draw_buttons = node_shader_buts_hair; break; + case SH_NODE_BSDF_HAIR_PRINCIPLED: + ntype->draw_buttons = node_shader_buts_principled_hair; + break; case SH_NODE_SCRIPT: ntype->draw_buttons = node_shader_buts_script; ntype->draw_buttons_ex = node_shader_buts_script_ex; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 00758bd1379..5c692cf85da 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -963,6 +963,11 @@ typedef struct NodeCryptomatte { #define SHD_HAIR_REFLECTION 0 #define SHD_HAIR_TRANSMISSION 1 +/* principled hair parametrization */ +#define SHD_PRINCIPLED_HAIR_REFLECTANCE 0 +#define SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION 1 +#define SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION 2 + /* blend texture */ #define SHD_BLEND_LINEAR 0 #define SHD_BLEND_QUADRATIC 1 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 52fbadd0f54..b41e9b1c4e5 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -3350,6 +3350,13 @@ static const EnumPropertyItem node_hair_items[] = { {0, NULL, 0, NULL, NULL} }; +static const EnumPropertyItem node_principled_hair_items[] = { + {SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION, "ABSORPTION", 0, "Absorption coefficient", "Directly set the absorption coefficient sigma_a. This is not the most intuitive way to color hair."}, + {SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION, "MELANIN", 0, "Melanin concentration", "Define the melanin concentrations below to get the most realistic-looking hair. You can get the concentrations for different types of hair online."}, + {SHD_PRINCIPLED_HAIR_REFLECTANCE, "COLOR", 0, "Direct coloring", "Choose the color of your preference, and the shader will approximate the absorption coefficient to render lookalike hair."}, + {0, NULL, 0, NULL, NULL} +}; + static const EnumPropertyItem node_script_mode_items[] = { {NODE_SCRIPT_INTERNAL, "INTERNAL", 0, "Internal", "Use internal text data-block"}, {NODE_SCRIPT_EXTERNAL, "EXTERNAL", 0, "External", "Use external .osl or .oso file"}, @@ -4409,6 +4416,21 @@ static void def_hair(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +/* RNA initialization for the custom property. */ +static void def_hair_principled(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "parametrization", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_ui_text(prop, "Color parametrization", "Select the shader's color parametrization"); + RNA_def_property_enum_items(prop, node_principled_hair_items); + RNA_def_property_enum_default(prop, SHD_PRINCIPLED_HAIR_REFLECTANCE); + /* Upon editing, update both the node data AND the UI representation */ + /* (This effectively shows/hides the relevant sockets) */ + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); +} + static void def_sh_uvmap(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 5a5d5997878..7ef41b25de8 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -173,6 +173,7 @@ set(SRC shader/nodes/node_shader_bsdf_transparent.c shader/nodes/node_shader_bsdf_velvet.c shader/nodes/node_shader_bsdf_hair.c + shader/nodes/node_shader_bsdf_hair_principled.c shader/nodes/node_shader_bump.c shader/nodes/node_shader_emission.c shader/nodes/node_shader_fresnel.c diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index ca604363e35..d4134f09597 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -116,6 +116,7 @@ void register_node_type_sh_volume_absorption(void); void register_node_type_sh_volume_scatter(void); void register_node_type_sh_volume_principled(void); void register_node_type_sh_bsdf_hair(void); +void register_node_type_sh_bsdf_hair_principled(void); void register_node_type_sh_subsurface_scattering(void); void register_node_type_sh_mix_shader(void); void register_node_type_sh_add_shader(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index a4f8a798576..2fa8cda56a5 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -89,6 +89,7 @@ DefNode( ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BS DefNode( ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "" ) DefNode( ShaderNode, SH_NODE_BSDF_TOON, def_toon, "BSDF_TOON", BsdfToon, "Toon BSDF", "" ) DefNode( ShaderNode, SH_NODE_BSDF_HAIR, def_hair, "BSDF_HAIR", BsdfHair, "Hair BSDF", "" ) +DefNode( ShaderNode, SH_NODE_BSDF_HAIR_PRINCIPLED, def_hair_principled, "BSDF_HAIR_PRINCIPLED", BsdfHairPrincipled, "Principled Hair BSDF", "") DefNode( ShaderNode, SH_NODE_SUBSURFACE_SCATTERING, def_sh_subsurface, "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","") DefNode( ShaderNode, SH_NODE_VOLUME_ABSORPTION, 0, "VOLUME_ABSORPTION", VolumeAbsorption, "Volume Absorption", "" ) DefNode( ShaderNode, SH_NODE_VOLUME_SCATTER, 0, "VOLUME_SCATTER", VolumeScatter, "Volume Scatter", "" ) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c new file mode 100644 index 00000000000..c5029852033 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c @@ -0,0 +1,133 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 + * of the License, or (at your option) any later version. + * + * 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) 2018 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Stockner, L. E. Segovia + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "../node_shader_util.h" + +/* **************** OUTPUT ******************** */ + +/* Color, melanin and absorption coefficient default to approximately same brownish hair. */ +static bNodeSocketTemplate sh_node_bsdf_hair_principled_in[] = { + { SOCK_RGBA, 1, N_("Color"), 0.017513f, 0.005763f, 0.002059f, 1.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 1, N_("Melanin"), 0.8f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Melanin Redness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_RGBA, 1, N_("Tint"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, + { SOCK_VECTOR, 1, N_("Absorption Coefficient"), 0.245531f, 0.52f, 1.365f, 0.0f, 0.0f, 1000.0f}, + { SOCK_FLOAT, 1, N_("Roughness"), 0.3f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Radial Roughness"), 0.3f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Coat"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("IOR"), 1.55f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, + { SOCK_FLOAT, 1, N_("Offset"), 2.f*((float)M_PI)/180.f, 0.0f, 0.0f, 0.0f, -M_PI_2, M_PI_2, PROP_ANGLE}, + { SOCK_FLOAT, 1, N_("Random Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Random Roughness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Random"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + { -1, 0, "" }, +}; + +static bNodeSocketTemplate sh_node_bsdf_hair_principled_out[] = { + { SOCK_SHADER, 0, N_("BSDF")}, + { -1, 0, "" } +}; + +/* Initialize the custom Parametrization property to Color. */ +static void node_shader_init_hair_principled(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = SHD_PRINCIPLED_HAIR_REFLECTANCE; +} + +/* Triggers (in)visibility of some sockets when changing Parametrization. */ +static void node_shader_update_hair_principled(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock; + int parametrization = node->custom1; + + for (sock = node->inputs.first; sock; sock = sock->next) { + if (STREQ(sock->name, "Color")) { + if (parametrization == SHD_PRINCIPLED_HAIR_REFLECTANCE) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Melanin")) { + if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Melanin Redness")) { + if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Tint")) { + if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Absorption Coefficient")) { + if (parametrization == SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + else if (STREQ(sock->name, "Random Color")) { + if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { + sock->flag &= ~SOCK_UNAVAIL; + } + else { + sock->flag |= SOCK_UNAVAIL; + } + } + } +} + +/* node type definition */ +void register_node_type_sh_bsdf_hair_principled(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_BSDF_HAIR_PRINCIPLED, "Principled Hair BSDF", NODE_CLASS_SHADER, 0); + node_type_compatibility(&ntype, NODE_NEW_SHADING); + node_type_socket_templates(&ntype, sh_node_bsdf_hair_principled_in, sh_node_bsdf_hair_principled_out); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_init(&ntype, node_shader_init_hair_principled); + node_type_storage(&ntype, "", NULL, NULL); + node_type_update(&ntype, node_shader_update_hair_principled, NULL); + + nodeRegisterType(&ntype); +} -- cgit v1.2.3