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 --- release/scripts/startup/nodeitems_builtins.py | 1 + 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 +++++++++++++++++++++ 30 files changed, 1066 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 diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 92411aeb0ef..c9a15f12f7c 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -394,6 +394,7 @@ compositor_node_categories = [ NodeItem("CompositorNodeChromaMatte"), NodeItem("CompositorNodeColorMatte"), NodeItem("CompositorNodeDoubleEdgeMask"), + NodeItem("CompositorNodeCryptomatte"), ]), CompositorNodeCategory("CMP_DISTORT", "Distort", items=[ NodeItem("CompositorNodeScale"), 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