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:
@@ -394,6 +394,7 @@ compositor_node_categories = [
|
||||
NodeItem("CompositorNodeChromaMatte"),
|
||||
NodeItem("CompositorNodeColorMatte"),
|
||||
NodeItem("CompositorNodeDoubleEdgeMask"),
|
||||
NodeItem("CompositorNodeCryptomatte"),
|
||||
]),
|
||||
CompositorNodeCategory("CMP_DISTORT", "Distort", items=[
|
||||
NodeItem("CompositorNodeScale"),
|
||||
|
||||
@@ -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);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -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();
|
||||
|
||||
40
source/blender/blenlib/BLI_hash_mm3.h
Normal file
40
source/blender/blenlib/BLI_hash_mm3.h
Normal 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__ */
|
||||
@@ -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
|
||||
|
||||
147
source/blender/blenlib/intern/hash_mm3.c
Normal file
147
source/blender/blenlib/intern/hash_mm3.c
Normal 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;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
120
source/blender/compositor/nodes/COM_CryptomatteNode.cpp
Normal file
120
source/blender/compositor/nodes/COM_CryptomatteNode.cpp
Normal 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));
|
||||
|
||||
}
|
||||
38
source/blender/compositor/nodes/COM_CryptomatteNode.h
Normal file
38
source/blender/compositor/nodes/COM_CryptomatteNode.h
Normal 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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 ************************/
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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", "" )
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user