/* SPDX-FileCopyrightText: 2007 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup nodes */ #include "DNA_node_types.h" #include "DNA_scene_types.h" #include "BLI_listbase.h" #include "BKE_context.hh" #include "BKE_global.hh" #include "BKE_image.hh" #include "BKE_main.hh" #include "BKE_node.hh" #include "BKE_node_runtime.hh" #include "BKE_node_tree_update.hh" #include "BKE_tracking.h" #include "UI_resources.hh" #include "node_common.h" #include "RNA_prototypes.hh" #include "NOD_composite.hh" #include "node_composite_util.hh" static void composite_get_from_context(const bContext *C, blender::bke::bNodeTreeType * /*treetype*/, bNodeTree **r_ntree, ID **r_id, ID **r_from) { Scene *scene = CTX_data_scene(C); *r_from = nullptr; *r_id = &scene->id; *r_ntree = scene->nodetree; } static void foreach_nodeclass(void *calldata, blender::bke::bNodeClassCallback func) { func(calldata, NODE_CLASS_INPUT, N_("Input")); func(calldata, NODE_CLASS_OUTPUT, N_("Output")); func(calldata, NODE_CLASS_OP_COLOR, N_("Color")); func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector")); func(calldata, NODE_CLASS_OP_FILTER, N_("Filter")); func(calldata, NODE_CLASS_CONVERTER, N_("Converter")); func(calldata, NODE_CLASS_MATTE, N_("Matte")); func(calldata, NODE_CLASS_DISTORT, N_("Distort")); func(calldata, NODE_CLASS_GROUP, N_("Group")); func(calldata, NODE_CLASS_INTERFACE, N_("Interface")); func(calldata, NODE_CLASS_LAYOUT, N_("Layout")); } /* local tree then owns all compbufs */ static void localize(bNodeTree *localtree, bNodeTree *ntree) { bNode *node = (bNode *)ntree->nodes.first; bNode *local_node = (bNode *)localtree->nodes.first; while (node != nullptr) { /* Ensure new user input gets handled ok. */ node->runtime->need_exec = 0; local_node->runtime->original = node; /* move over the compbufs */ /* right after #blender::bke::node_tree_copy_tree() `oldsock` pointers are valid */ if (node->type_legacy == CMP_NODE_VIEWER) { if (node->id) { if (node->flag & NODE_DO_OUTPUT) { local_node->id = (ID *)node->id; } else { local_node->id = nullptr; } } } node = node->next; local_node = local_node->next; } } static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) { /* move over the compbufs and previews */ blender::bke::node_preview_merge_tree(ntree, localtree, true); LISTBASE_FOREACH (bNode *, lnode, &localtree->nodes) { if (bNode *orig_node = blender::bke::node_find_node_by_name(*ntree, lnode->name)) { if (lnode->type_legacy == CMP_NODE_VIEWER) { if (lnode->id && (lnode->flag & NODE_DO_OUTPUT)) { /* image_merge does sanity check for pointers */ BKE_image_merge(bmain, (Image *)orig_node->id, (Image *)lnode->id); } } else if (lnode->type_legacy == CMP_NODE_MOVIEDISTORTION) { /* special case for distortion node: distortion context is allocating in exec function * and to achieve much better performance on further calls this context should be * copied back to original node */ if (lnode->storage) { if (orig_node->storage) { BKE_tracking_distortion_free((MovieDistortion *)orig_node->storage); } orig_node->storage = BKE_tracking_distortion_copy((MovieDistortion *)lnode->storage); } } } } } static void update(bNodeTree *ntree) { blender::bke::node_tree_set_output(*ntree); ntree_update_reroute_nodes(ntree); } static void composite_node_add_init(bNodeTree * /*bnodetree*/, bNode *bnode) { /* Composite node will only show previews for input classes * by default, other will be hidden * but can be made visible with the show_preview option */ if (bnode->typeinfo->nclass != NODE_CLASS_INPUT) { bnode->flag &= ~NODE_PREVIEW; } } static bool composite_node_tree_socket_type_valid(blender::bke::bNodeTreeType * /*ntreetype*/, blender::bke::bNodeSocketType *socket_type) { return blender::bke::node_is_static_socket_type(*socket_type) && ELEM(socket_type->type, SOCK_FLOAT, SOCK_INT, SOCK_VECTOR, SOCK_RGBA); } static bool composite_validate_link(eNodeSocketDatatype /*from*/, eNodeSocketDatatype /*to*/) { /* All supported types can be implicitly converted to other types. */ return true; } blender::bke::bNodeTreeType *ntreeType_Composite; void register_node_tree_type_cmp() { blender::bke::bNodeTreeType *tt = ntreeType_Composite = MEM_new( __func__); tt->type = NTREE_COMPOSIT; tt->idname = "CompositorNodeTree"; tt->group_idname = "CompositorNodeGroup"; tt->ui_name = N_("Compositor"); tt->ui_icon = ICON_NODE_COMPOSITING; tt->ui_description = N_("Compositing nodes"); tt->foreach_nodeclass = foreach_nodeclass; tt->localize = localize; tt->local_merge = local_merge; tt->update = update; tt->get_from_context = composite_get_from_context; tt->node_add_init = composite_node_add_init; tt->validate_link = composite_validate_link; tt->valid_socket_type = composite_node_tree_socket_type_valid; tt->rna_ext.srna = &RNA_CompositorNodeTree; blender::bke::node_tree_type_add(*tt); } /* *********************************************** */ void ntreeCompositUpdateRLayers(bNodeTree *ntree) { if (ntree == nullptr) { return; } for (bNode *node : ntree->all_nodes()) { if (node->type_legacy == CMP_NODE_R_LAYERS) { node_cmp_rlayers_outputs(ntree, node); } else if (node->type_legacy == CMP_NODE_CRYPTOMATTE && node->custom1 == CMP_NODE_CRYPTOMATTE_SOURCE_RENDER) { node->typeinfo->updatefunc(ntree, node); } } } void ntreeCompositTagRender(Scene *scene) { /* XXX Think using G_MAIN here is valid, since you want to update current file's scene nodes, * not the ones in temp main generated for rendering? * This is still rather weak though, * ideally render struct would store its own main AND original G_MAIN. */ for (Scene *sce_iter = (Scene *)G_MAIN->scenes.first; sce_iter; sce_iter = (Scene *)sce_iter->id.next) { if (sce_iter->nodetree) { for (bNode *node : sce_iter->nodetree->all_nodes()) { if (node->id == (ID *)scene || node->type_legacy == CMP_NODE_COMPOSITE) { BKE_ntree_update_tag_node_property(sce_iter->nodetree, node); } else if (node->type_legacy == CMP_NODE_TEXTURE) /* uses scene size_x/size_y */ { BKE_ntree_update_tag_node_property(sce_iter->nodetree, node); } } } } BKE_ntree_update(*G_MAIN); } void ntreeCompositClearTags(bNodeTree *ntree) { /* XXX: after render animation system gets a refresh, this call allows composite to end clean. */ if (ntree == nullptr) { return; } for (bNode *node : ntree->all_nodes()) { node->runtime->need_exec = 0; if (node->is_group()) { ntreeCompositClearTags((bNodeTree *)node->id); } } } void ntreeCompositTagNeedExec(bNode *node) { node->runtime->need_exec = true; }