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 --- .../composite/nodes/node_composite_cryptomatte.c | 309 +++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 source/blender/nodes/composite/nodes/node_composite_cryptomatte.c (limited to 'source/blender/nodes/composite') diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c new file mode 100644 index 00000000000..488dfa6756e --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c @@ -0,0 +1,309 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Stockner, Stefan Werner + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/nodes/composite/nodes/node_composite_cryptomatte.c + * \ingroup cmpnodes + */ + +#include "node_composite_util.h" +#include "BLI_dynstr.h" +#include "BLI_hash_mm3.h" +#include "BLI_assert.h" + +#ifndef max + #define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min + #define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* this is taken from the cryptomatte specification 1.0 */ + +static inline float hash_to_float(uint32_t hash) +{ + uint32_t mantissa = hash & (( 1 << 23) - 1); + uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); + exponent = max(exponent, (uint32_t) 1); + exponent = min(exponent, (uint32_t) 254); + exponent = exponent << 23; + uint32_t sign = (hash >> 31); + sign = sign << 31; + uint32_t float_bits = sign | exponent | mantissa; + float f; + /* Bit casting relies on equal size for both types. */ + BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float and uint32_t are not the same size") + memcpy(&f, &float_bits, sizeof(float)); + return f; +} + +static void cryptomatte_add(NodeCryptomatte* n, float f) +{ + /* Turn the number into a string. */ + char number[32]; + BLI_snprintf(number, sizeof(number), "<%.9g>", f); + + /* Search if we already have the number. */ + if (n->matte_id && strlen(n->matte_id) != 0) { + size_t start = 0; + const size_t end = strlen(n->matte_id); + size_t token_len = 0; + while (start < end) { + /* Ignore leading whitespace. */ + while (start < end && n->matte_id[start] == ' ') { + ++start; + } + + /* Find the next seprator. */ + char* token_end = strchr(n->matte_id+start, ','); + if (token_end == NULL || token_end == n->matte_id+start) { + token_end = n->matte_id+end; + } + /* Be aware that token_len still contains any trailing white space. */ + token_len = token_end - (n->matte_id + start); + + /* If this has a leading bracket, assume a raw floating point number and look for the closing bracket. */ + if (n->matte_id[start] == '<') { + if (strncmp(n->matte_id+start, number, strlen(number)) == 0) { + /* This number is already there, so continue. */ + return; + } + } + else { + /* Remove trailing white space */ + size_t name_len = token_len; + while (n->matte_id[start+name_len] == ' ' && name_len > 0) { + name_len--; + } + /* Calculate the hash of the token and compare. */ + uint32_t hash = BLI_hash_mm3((const unsigned char*)(n->matte_id+start), name_len, 0); + if (f == hash_to_float(hash)) { + return; + } + } + start += token_len+1; + } + } + + DynStr *new_matte = BLI_dynstr_new(); + if (!new_matte) { + return; + } + + if(n->matte_id) { + BLI_dynstr_append(new_matte, n->matte_id); + MEM_freeN(n->matte_id); + } + + if(BLI_dynstr_get_len(new_matte) > 0) { + BLI_dynstr_append(new_matte, ","); + } + BLI_dynstr_append(new_matte, number); + n->matte_id = BLI_dynstr_get_cstring(new_matte); + BLI_dynstr_free(new_matte); +} + +static void cryptomatte_remove(NodeCryptomatte*n, float f) +{ + if (n->matte_id == NULL || strlen(n->matte_id) == 0) { + /* Empty string, nothing to remove. */ + return; + } + + /* This will be the new string without the removed key. */ + DynStr *new_matte = BLI_dynstr_new(); + if (!new_matte) { + return; + } + + /* Turn the number into a string. */ + static char number[32]; + BLI_snprintf(number, sizeof(number), "<%.9g>", f); + + /* Search if we already have the number. */ + size_t start = 0; + const size_t end = strlen(n->matte_id); + size_t token_len = 0; + bool is_first = true; + while (start < end) { + bool skip = false; + /* Ignore leading whitespace or commas. */ + while (start < end && ((n->matte_id[start] == ' ') || (n->matte_id[start] == ','))) { + ++start; + } + + /* Find the next seprator. */ + char* token_end = strchr(n->matte_id+start+1, ','); + if (token_end == NULL || token_end == n->matte_id+start) { + token_end = n->matte_id+end; + } + /* Be aware that token_len still contains any trailing white space. */ + token_len = token_end - (n->matte_id + start); + + if (token_len == 1) { + skip = true; + } + /* If this has a leading bracket, assume a raw floating point number and look for the closing bracket. */ + else if (n->matte_id[start] == '<') { + if (strncmp(n->matte_id+start, number, strlen(number)) == 0) { + /* This number is already there, so skip it. */ + skip = true; + } + } + else { + /* Remove trailing white space */ + size_t name_len = token_len; + while (n->matte_id[start+name_len] == ' ' && name_len > 0) { + name_len--; + } + /* Calculate the hash of the token and compare. */ + uint32_t hash = BLI_hash_mm3((const unsigned char*)(n->matte_id+start), name_len, 0); + if (f == hash_to_float(hash)) { + skip = true; + } + } + if (!skip) { + if (is_first) { + is_first = false; + } + else { + BLI_dynstr_append(new_matte, ", "); + } + BLI_dynstr_nappend(new_matte, n->matte_id+start, token_len); + } + start += token_len+1; + } + + if(n->matte_id) { + MEM_freeN(n->matte_id); + n->matte_id = NULL; + } + if(BLI_dynstr_get_len(new_matte) > 0) { + n->matte_id = BLI_dynstr_get_cstring(new_matte); + } + BLI_dynstr_free(new_matte); +} + +static bNodeSocketTemplate outputs[] = { + { SOCK_RGBA, 0, N_("Image")}, + { SOCK_FLOAT, 0, N_("Matte")}, + { SOCK_RGBA, 0, N_("Pick")}, + { -1, 0, "" } +}; + +void ntreeCompositCryptomatteSyncFromAdd(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeCryptomatte *n = node->storage; + if (n->add[0] != 0.0f) { + cryptomatte_add(n, n->add[0]); + n->add[0] = 0.0f; + n->add[1] = 0.0f; + n->add[2] = 0.0f; + } +} + +void ntreeCompositCryptomatteSyncFromRemove(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeCryptomatte *n = node->storage; + if (n->remove[0] != 0.0f) { + cryptomatte_remove(n, n->remove[0]); + n->remove[0] = 0.0f; + n->remove[1] = 0.0f; + n->remove[2] = 0.0f; + } +} + +bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node) +{ + NodeCryptomatte *n = node->storage; + char sockname[32]; + n->num_inputs++; + BLI_snprintf(sockname, sizeof(sockname), "Crypto %.2d", n->num_inputs-1); + bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, sockname); + return sock; +} + +int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node) +{ + NodeCryptomatte *n = node->storage; + if (n->num_inputs < 2) { + return 0; + } + bNodeSocket *sock = node->inputs.last; + nodeRemoveSocket(ntree, node, sock); + n->num_inputs--; + return 1; +} + +static void init(bNodeTree *ntree, bNode *node) +{ + NodeCryptomatte *user = MEM_callocN(sizeof(NodeCryptomatte), "cryptomatte user"); + node->storage = user; + + + nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, "image", "Image"); + + /* Add three inputs by default, as recommended by the Cryptomatte specification. */ + ntreeCompositCryptomatteAddSocket(ntree, node); + ntreeCompositCryptomatteAddSocket(ntree, node); + ntreeCompositCryptomatteAddSocket(ntree, node); +} + +static void node_free_cryptomatte(bNode *node) +{ + NodeCryptomatte *nc = node->storage; + + if (nc) { + if (nc->matte_id) { + MEM_freeN(nc->matte_id); + } + + MEM_freeN(nc); + } +} + +static void node_copy_cryptomatte(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, bNode *src_node) +{ + NodeCryptomatte *src_nc = src_node->storage; + NodeCryptomatte *dest_nc = MEM_dupallocN(src_nc); + + if (src_nc->matte_id) + dest_nc->matte_id = MEM_dupallocN(src_nc->matte_id); + + dest_node->storage = dest_nc; +} + +void register_node_type_cmp_cryptomatte(void) +{ + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE, "Cryptomatte", NODE_CLASS_CONVERTOR, 0); + node_type_socket_templates(&ntype, NULL, outputs); + node_type_init(&ntype, init); + node_type_storage(&ntype, "NodeCryptomatte", node_free_cryptomatte, node_copy_cryptomatte); + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From 566b319335563888e252b2186c93606ad41ff216 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 18 Jul 2018 13:34:22 +0200 Subject: Cleanup: Cryptomatte node style tweaks. --- .../nodes/composite/nodes/node_composite_cryptomatte.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'source/blender/nodes/composite') diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c index 488dfa6756e..0231e4717b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c @@ -4,7 +4,7 @@ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. + * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -30,17 +30,10 @@ */ #include "node_composite_util.h" +#include "BLI_assert.h" #include "BLI_dynstr.h" #include "BLI_hash_mm3.h" -#include "BLI_assert.h" - -#ifndef max - #define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef min - #define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif +#include "BLI_utildefines.h" /* this is taken from the cryptomatte specification 1.0 */ @@ -48,8 +41,8 @@ static inline float hash_to_float(uint32_t hash) { uint32_t mantissa = hash & (( 1 << 23) - 1); uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); - exponent = max(exponent, (uint32_t) 1); - exponent = min(exponent, (uint32_t) 254); + exponent = MAX2(exponent, (uint32_t) 1); + exponent = MIN2(exponent, (uint32_t) 254); exponent = exponent << 23; uint32_t sign = (hash >> 31); sign = sign << 31; -- cgit v1.2.3