Compositor: Cryptomatte compositing node.
authorStefan Werner <stefan.werner@tangent-animation.com>
Wed, 18 Jul 2018 11:03:09 +0000 (13:03 +0200)
committerStefan Werner <stefan.werner@tangent-animation.com>
Wed, 18 Jul 2018 11:03:34 +0000 (13:03 +0200)
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

30 files changed:
release/scripts/startup/nodeitems_builtins.py
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/blenlib/BLI_hash_mm3.h [new file with mode: 0644]
source/blender/blenlib/CMakeLists.txt
source/blender/blenlib/intern/hash_mm3.c [new file with mode: 0644]
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/compositor/CMakeLists.txt
source/blender/compositor/intern/COM_Converter.cpp
source/blender/compositor/nodes/COM_CryptomatteNode.cpp [new file with mode: 0644]
source/blender/compositor/nodes/COM_CryptomatteNode.h [new file with mode: 0644]
source/blender/compositor/operations/COM_CryptomatteOperation.cpp [new file with mode: 0644]
source/blender/compositor/operations/COM_CryptomatteOperation.h [new file with mode: 0644]
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface_eyedropper.c
source/blender/editors/interface/interface_eyedropper_color.c
source/blender/editors/interface/interface_intern.h
source/blender/editors/interface/interface_ops.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_node/node_edit.c
source/blender/editors/space_node/node_intern.h
source/blender/editors/space_node/node_ops.c
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_composite.h
source/blender/nodes/NOD_static_types.h
source/blender/nodes/composite/nodes/node_composite_cryptomatte.c [new file with mode: 0644]

index 92411ae..c9a15f1 100644 (file)
@@ -394,6 +394,7 @@ compositor_node_categories = [
         NodeItem("CompositorNodeChromaMatte"),
         NodeItem("CompositorNodeColorMatte"),
         NodeItem("CompositorNodeDoubleEdgeMask"),
+        NodeItem("CompositorNodeCryptomatte"),
     ]),
     CompositorNodeCategory("CMP_DISTORT", "Distort", items=[
         NodeItem("CompositorNodeScale"),
index eca7f82..8e54c6a 100644 (file)
@@ -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);
+
 /** \} */
 
 /* -------------------------------------------------------------------- */
index 499f92b..f15d901 100644 (file)
@@ -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 (file)
index 0000000..93bf963
--- /dev/null
@@ -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__ */
index 16497c1..e3f5773 100644 (file)
@@ -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 (file)
index 0000000..ac48379
--- /dev/null
@@ -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 <stdlib.h>
+#  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;
+}
index 4e845b9..1def462 100644 (file)
@@ -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)
index e3a901f..4b64d0a 100644 (file)
@@ -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);
                        }
index 3e1dd83..0ad53d3 100644 (file)
@@ -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
index 58e0da0..c918190 100644 (file)
@@ -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 (file)
index 0000000..c813406
--- /dev/null
@@ -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 <iterator>
+
+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 (file)
index 0000000..5251b57
--- /dev/null
@@ -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 (file)
index 0000000..9dd3686
--- /dev/null
@@ -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 (file)
index 0000000..9ce02c0
--- /dev/null
@@ -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<float> m_objectIndex;
+public:
+       std::vector<SocketReader *> 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
index 51f79e0..eefbeee 100644 (file)
@@ -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);
index fafe5f4..67486f3 100644 (file)
@@ -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");
index 7dd7de0..bcb6001 100644 (file)
@@ -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;
 }
index cc86530..dc5e100 100644 (file)
@@ -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);
index 9a4ea41..1e67ecd 100644 (file)
@@ -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);
index bd4bb03..daee0d3 100644 (file)
@@ -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 ************************/
 
index b0440b3..23df1b7 100644 (file)
@@ -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;
        }
index c49c8c2..97c5157 100644 (file)
@@ -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;
+}
index fd62c52..9b396aa 100644 (file)
@@ -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
index d7ded62..ea07341 100644 (file)
@@ -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)
index 1291723..00758bd 100644 (file)
@@ -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
index e66c1e9..52fbadd 100644 (file)
@@ -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)
index cc0bef3..5a5d599 100644 (file)
@@ -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
index a5c2e60..2a82b70 100644 (file)
@@ -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);
index 5217c7d..a4f8a79 100644 (file)
@@ -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 (file)
index 0000000..488dfa6
--- /dev/null
@@ -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);
+}