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
This commit is contained in:
Stefan Werner
2018-07-18 13:03:09 +02:00
parent 41045478ab
commit bdda0964e0
30 changed files with 1067 additions and 43 deletions

View File

@@ -394,6 +394,7 @@ compositor_node_categories = [
NodeItem("CompositorNodeChromaMatte"), NodeItem("CompositorNodeChromaMatte"),
NodeItem("CompositorNodeColorMatte"), NodeItem("CompositorNodeColorMatte"),
NodeItem("CompositorNodeDoubleEdgeMask"), NodeItem("CompositorNodeDoubleEdgeMask"),
NodeItem("CompositorNodeCryptomatte"),
]), ]),
CompositorNodeCategory("CMP_DISTORT", "Distort", items=[ CompositorNodeCategory("CMP_DISTORT", "Distort", items=[
NodeItem("CompositorNodeScale"), NodeItem("CompositorNodeScale"),

View File

@@ -945,6 +945,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria
#define CMP_NODE_PLANETRACKDEFORM 320 #define CMP_NODE_PLANETRACKDEFORM 320
#define CMP_NODE_CORNERPIN 321 #define CMP_NODE_CORNERPIN 321
#define CMP_NODE_SWITCH_VIEW 322 #define CMP_NODE_SWITCH_VIEW 322
#define CMP_NODE_CRYPTOMATTE 323
/* channel toggles */ /* channel toggles */
#define CMP_CHAN_RGB 1 #define CMP_CHAN_RGB 1
@@ -997,6 +998,11 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, struct bNodeSocke
void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node);
void ntreeCompositColorBalanceSyncFromCDL(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);
/** \} */ /** \} */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */

View File

@@ -3528,6 +3528,7 @@ static void registerCompositNodes(void)
register_node_type_cmp_doubleedgemask(); register_node_type_cmp_doubleedgemask();
register_node_type_cmp_keyingscreen(); register_node_type_cmp_keyingscreen();
register_node_type_cmp_keying(); register_node_type_cmp_keying();
register_node_type_cmp_cryptomatte();
register_node_type_cmp_translate(); register_node_type_cmp_translate();
register_node_type_cmp_rotate(); register_node_type_cmp_rotate();

View File

@@ -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__ */

View File

@@ -74,6 +74,7 @@ set(SRC
intern/gsqueue.c intern/gsqueue.c
intern/hash_md5.c intern/hash_md5.c
intern/hash_mm2a.c intern/hash_mm2a.c
intern/hash_mm3.c
intern/jitter_2d.c intern/jitter_2d.c
intern/lasso_2d.c intern/lasso_2d.c
intern/list_sort_impl.h intern/list_sort_impl.h
@@ -159,6 +160,7 @@ set(SRC
BLI_hash.h BLI_hash.h
BLI_hash_md5.h BLI_hash_md5.h
BLI_hash_mm2a.h BLI_hash_mm2a.h
BLI_hash_mm3.h
BLI_heap.h BLI_heap.h
BLI_jitter_2d.h BLI_jitter_2d.h
BLI_kdopbvh.h BLI_kdopbvh.h

View File

@@ -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;
}

View File

@@ -3161,6 +3161,10 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree)
direct_link_curvemapping(fd, node->storage); direct_link_curvemapping(fd, node->storage);
else if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) else if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
((ImageUser *)node->storage)->ok = 1; ((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) { else if ( ntree->type==NTREE_TEXTURE) {
if (node->type==TEX_NODE_CURVE_RGB || node->type==TEX_NODE_CURVE_TIME) if (node->type==TEX_NODE_CURVE_RGB || node->type==TEX_NODE_CURVE_TIME)

View File

@@ -1077,6 +1077,13 @@ static void write_nodetree_nolib(WriteData *wd, bNodeTree *ntree)
} }
writestruct_id(wd, DATA, node->typeinfo->storagename, 1, node->storage); 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 { else {
writestruct_id(wd, DATA, node->typeinfo->storagename, 1, node->storage); writestruct_id(wd, DATA, node->typeinfo->storagename, 1, node->storage);
} }

View File

@@ -182,6 +182,11 @@ set(SRC
operations/COM_SunBeamsOperation.cpp operations/COM_SunBeamsOperation.cpp
operations/COM_SunBeamsOperation.h 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.cpp
nodes/COM_CornerPinNode.h nodes/COM_CornerPinNode.h
nodes/COM_PlaneTrackDeformNode.cpp nodes/COM_PlaneTrackDeformNode.cpp

View File

@@ -55,6 +55,7 @@ extern "C" {
#include "COM_Converter.h" #include "COM_Converter.h"
#include "COM_CornerPinNode.h" #include "COM_CornerPinNode.h"
#include "COM_CropNode.h" #include "COM_CropNode.h"
#include "COM_CryptomatteNode.h"
#include "COM_DefocusNode.h" #include "COM_DefocusNode.h"
#include "COM_DespeckleNode.h" #include "COM_DespeckleNode.h"
#include "COM_DifferenceMatteNode.h" #include "COM_DifferenceMatteNode.h"
@@ -406,6 +407,9 @@ Node *Converter::convert(bNode *b_node)
case CMP_NODE_SUNBEAMS: case CMP_NODE_SUNBEAMS:
node = new SunBeamsNode(b_node); node = new SunBeamsNode(b_node);
break; break;
case CMP_NODE_CRYPTOMATTE:
node = new CryptomatteNode(b_node);
break;
} }
return node; return node;
} }

View File

@@ -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));
}

View File

@@ -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

View File

@@ -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];
}
}
}
}

View File

@@ -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

View File

@@ -969,6 +969,7 @@ void uiTemplateCurveMapping(
bool levels, bool brush, bool neg_slope); 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 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 uiTemplatePalette(uiLayout *layout, struct PointerRNA *ptr, const char *propname, bool color);
void uiTemplateCryptoPicker(uiLayout *layout, struct PointerRNA *ptr, const char *propname);
void uiTemplateLayers( void uiTemplateLayers(
uiLayout *layout, struct PointerRNA *ptr, const char *propname, uiLayout *layout, struct PointerRNA *ptr, const char *propname,
PointerRNA *used_ptr, const char *used_propname, int active_layer); PointerRNA *used_ptr, const char *used_propname, int active_layer);

View File

@@ -82,6 +82,7 @@ wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
/* assign to operators */ /* assign to operators */
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorband"); WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_colorband");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color"); 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_id");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth"); WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver"); WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver");

View File

@@ -36,6 +36,7 @@
#include "DNA_screen_types.h" #include "DNA_screen_types.h"
#include "BLI_math_vector.h" #include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BKE_context.h" #include "BKE_context.h"
#include "BKE_main.h" #include "BKE_main.h"
@@ -72,6 +73,8 @@ typedef struct Eyedropper {
bool accum_start; /* has mouse been pressed */ bool accum_start; /* has mouse been pressed */
float accum_col[3]; float accum_col[3];
int accum_tot; int accum_tot;
bool accumulate; /* Color picking for cryptomatte, without accumulation. */
} Eyedropper; } Eyedropper;
static bool eyedropper_init(bContext *C, wmOperator *op) static bool eyedropper_init(bContext *C, wmOperator *op)
@@ -80,6 +83,7 @@ static bool eyedropper_init(bContext *C, wmOperator *op)
Eyedropper *eye; Eyedropper *eye;
op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper"); 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); 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); 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) static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
{ {
/* Accumulate color. */
float col[3]; float col[3];
eyedropper_color_sample_fl(C, mx, my, col); 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) if (eye->accumulate) {
{ add_v3_v3(eye->accum_col, col);
float col[3]; eye->accum_tot++;
eyedropper_color_sample_fl(C, mx, my, col); }
/* delay linear conversion */ else {
add_v3_v3(eye->accum_col, col); copy_v3_v3(eye->accum_col, col);
eye->accum_tot++; 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) 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) { if (eye->accum_tot == 0) {
eyedropper_color_sample(C, eye, event->x, event->y); eyedropper_color_sample(C, eye, event->x, event->y);
} }
else {
eyedropper_color_set_accum(C, eye);
}
eyedropper_exit(C, op); eyedropper_exit(C, op);
return OPERATOR_FINISHED; return OPERATOR_FINISHED;
case EYE_MODAL_SAMPLE_BEGIN: case EYE_MODAL_SAMPLE_BEGIN:
/* enable accum and make first sample */ /* enable accum and make first sample */
eye->accum_start = true; eye->accum_start = true;
eyedropper_color_sample_accum(C, eye, event->x, event->y); eyedropper_color_sample(C, eye, event->x, event->y);
break; break;
case EYE_MODAL_SAMPLE_RESET: case EYE_MODAL_SAMPLE_RESET:
eye->accum_tot = 0; eye->accum_tot = 0;
zero_v3(eye->accum_col); zero_v3(eye->accum_col);
eyedropper_color_sample_accum(C, eye, event->x, event->y); eyedropper_color_sample(C, eye, event->x, event->y);
eyedropper_color_set_accum(C, eye);
break; break;
} }
} }
else if (event->type == MOUSEMOVE) { else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (eye->accum_start) { if (eye->accum_start) {
/* button is pressed so keep sampling */ /* button is pressed so keep sampling */
eyedropper_color_sample_accum(C, eye, event->x, event->y); eyedropper_color_sample(C, eye, event->x, event->y);
eyedropper_color_set_accum(C, eye);
} }
} }
@@ -321,20 +321,9 @@ static int eyedropper_exec(bContext *C, wmOperator *op)
static bool eyedropper_poll(bContext *C) static bool eyedropper_poll(bContext *C)
{ {
PointerRNA ptr; /* Actual test for active button happens later, since we don't
PropertyRNA *prop; * know which one is active until mouse over. */
int index_dummy; return (CTX_wm_window(C) != NULL);
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;
} }
void UI_OT_eyedropper_color(wmOperatorType *ot) void UI_OT_eyedropper_color(wmOperatorType *ot)
@@ -353,6 +342,22 @@ void UI_OT_eyedropper_color(wmOperatorType *ot)
/* flags */ /* flags */
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL; ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
}
/* properties */
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";
/* 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;
} }

View File

@@ -763,6 +763,7 @@ struct wmKeyMap *eyedropper_colorband_modal_keymap(struct wmKeyConfig *keyconf);
/* interface_eyedropper_color.c */ /* interface_eyedropper_color.c */
void UI_OT_eyedropper_color(struct wmOperatorType *ot); void UI_OT_eyedropper_color(struct wmOperatorType *ot);
void UI_OT_eyedropper_color_crypto(struct wmOperatorType *ot);
/* interface_eyedropper_colorband.c */ /* interface_eyedropper_colorband.c */
void UI_OT_eyedropper_colorband(struct wmOperatorType *ot); void UI_OT_eyedropper_colorband(struct wmOperatorType *ot);

View File

@@ -1138,6 +1138,7 @@ void ED_operatortypes_ui(void)
/* external */ /* external */
WM_operatortype_append(UI_OT_eyedropper_color); 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);
WM_operatortype_append(UI_OT_eyedropper_colorband_point); WM_operatortype_append(UI_OT_eyedropper_colorband_point);
WM_operatortype_append(UI_OT_eyedropper_id); WM_operatortype_append(UI_OT_eyedropper_id);

View 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 ************************/ /********************* Layer Buttons Template ************************/

View 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); 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) static void node_composit_buts_brightcontrast(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{ {
uiItemR(layout, ptr, "use_premultiply", 0, NULL, ICON_NONE); 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: case CMP_NODE_SUNBEAMS:
ntype->draw_buttons = node_composit_buts_sunbeams; ntype->draw_buttons = node_composit_buts_sunbeams;
break; 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: case CMP_NODE_BRIGHTCONTRAST:
ntype->draw_buttons = node_composit_buts_brightcontrast; ntype->draw_buttons = node_composit_buts_brightcontrast;
} }

View File

@@ -2611,3 +2611,93 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot)
/* flags */ /* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; 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;
}

View 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_viewer_border(struct wmOperatorType *ot);
void NODE_OT_clear_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[]; extern const char *node_context_dir[];
// XXXXXX // XXXXXX

View File

@@ -130,6 +130,9 @@ void node_operatortypes(void)
WM_operatortype_append(NODE_OT_tree_socket_add); WM_operatortype_append(NODE_OT_tree_socket_add);
WM_operatortype_append(NODE_OT_tree_socket_remove); WM_operatortype_append(NODE_OT_tree_socket_remove);
WM_operatortype_append(NODE_OT_tree_socket_move); 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) void ED_operatormacros_node(void)

View File

@@ -907,6 +907,14 @@ typedef struct NodeSunBeams {
float ray_length; float ray_length;
} NodeSunBeams; } NodeSunBeams;
typedef struct NodeCryptomatte {
float add[3];
float remove[3];
char *matte_id;
int num_inputs;
int pad;
} NodeCryptomatte;
/* script node mode */ /* script node mode */
#define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1 #define NODE_SCRIPT_EXTERNAL 1

View File

@@ -2880,6 +2880,48 @@ static void rna_NodeColorBalance_update_cdl(Main *bmain, Scene *scene, PointerRN
rna_Node_update(bmain, scene, ptr); 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 ******** */ /* ******** Node Socket Types ******** */
static PointerRNA rna_NodeOutputFile_slot_layer_get(CollectionPropertyIterator *iter) 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"); 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 --------------------------------------------------------- */ /* -- Texture Nodes --------------------------------------------------------- */
static void def_tex_output(StructRNA *srna) static void def_tex_output(StructRNA *srna)

View File

@@ -59,6 +59,7 @@ set(SRC
composite/nodes/node_composite_composite.c composite/nodes/node_composite_composite.c
composite/nodes/node_composite_cornerpin.c composite/nodes/node_composite_cornerpin.c
composite/nodes/node_composite_crop.c composite/nodes/node_composite_crop.c
composite/nodes/node_composite_cryptomatte.c
composite/nodes/node_composite_curves.c composite/nodes/node_composite_curves.c
composite/nodes/node_composite_despeckle.c composite/nodes/node_composite_despeckle.c
composite/nodes/node_composite_doubleEdgeMask.c composite/nodes/node_composite_doubleEdgeMask.c

View 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_doubleedgemask(void);
void register_node_type_cmp_keyingscreen(void); void register_node_type_cmp_keyingscreen(void);
void register_node_type_cmp_keying(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_translate(void);
void register_node_type_cmp_rotate(void); void register_node_type_cmp_rotate(void);

View 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_PLANETRACKDEFORM,def_cmp_planetrackdeform,"PLANETRACKDEFORM",PlaneTrackDeform,"Plane Track Deform","" )
DefNode( CompositorNode, CMP_NODE_CORNERPIN, 0, "CORNERPIN", CornerPin, "Corner Pin", "" ) 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_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_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode( TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) DefNode( TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )

View File

@@ -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);
}