Files
test/source/blender/blenloader/intern/versioning_common.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

535 lines
18 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
/* allow readfile to use deprecated functionality */
#define DNA_DEPRECATED_ALLOW
#include <cstring>
#include "DNA_node_types.h"
#include "DNA_screen_types.h"
#include "BLI_listbase.h"
#include "BLI_map.hh"
#include "BLI_string.h"
#include "BLI_string_ref.hh"
#include "BKE_animsys.h"
#include "BKE_idprop.h"
#include "BKE_ipo.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.hh"
#include "BKE_main.h"
#include "BKE_main_namemap.h"
Mesh: Replace auto smooth with node group Design task: #93551 This PR replaces the auto smooth option with a geometry nodes modifier that sets the sharp edge attribute. This solves a fair number of long- standing problems related to auto smooth, simplifies the process of normal computation, and allows Blender to automatically choose between face, vertex, and face corner normals based on the sharp edge and face attributes. Versioning adds a geometry node group to objects with meshes that had auto-smooth enabled. The modifier can be applied, which also improves performance. Auto smooth is now unnecessary to get a combination of sharp and smooth edges. In general workflows are changed a bit. Separate procedural and destructive workflows are available. Custom normals can be used immediately without turning on the removed auto smooth option. **Procedural** The node group asset "Smooth by Angle" is the main way to set sharp normals based on the edge angle. It can be accessed directly in the add modifier menu. Of course the modifier can be reordered, muted, or applied like any other, or changed internally like any geometry nodes modifier. **Destructive** Often the sharp edges don't need to be dynamic. This can give better performance since edge angles don't need to be recalculated. In edit mode the two operators "Select Sharp Edges" and "Mark Sharp" can be used. In other modes, the "Shade Smooth by Angle" controls the edge sharpness directly. ### Breaking API Changes - `use_auto_smooth` is removed. Face corner normals are now used automatically if there are mixed smooth vs. not smooth tags. Meshes now always use custom normals if they exist. - In Cycles, the lack of the separate auto smooth state makes normals look triangulated when all faces are shaded smooth. - `auto_smooth_angle` is removed. Replaced by a modifier (or operator) controlling the sharp edge attribute. This means the mesh itself (without an object) doesn't know anything about automatically smoothing by angle anymore. - `create_normals_split`, `calc_normals_split`, and `free_normals_split` are removed, and are replaced by the simpler `Mesh.corner_normals` collection property. Since it gives access to the normals cache, it is automatically updated when relevant data changes. Addons are updated here: https://projects.blender.org/blender/blender-addons/pulls/104609 ### Tests - `geo_node_curves_test_deform_curves_on_surface` has slightly different results because face corner normals are used instead of interpolated vertex normals. - `bf_wavefront_obj_tests` has different export results for one file which mixed sharp and smooth faces without turning on auto smooth. - `cycles_mesh_cpu` has one object which is completely flat shaded. Previously every edge was split before rendering, now it looks triangulated. Pull Request: https://projects.blender.org/blender/blender/pulls/108014
2023-10-20 16:54:08 +02:00
#include "BKE_mesh_legacy_convert.hh"
#include "BKE_node.hh"
#include "BKE_node_runtime.hh"
#include "SEQ_sequencer.h"
#include "MEM_guardedalloc.h"
#include "BLO_readfile.h"
#include "readfile.hh"
#include "versioning_common.hh"
using blender::Map;
using blender::StringRef;
ARegion *do_versions_add_region_if_not_found(ListBase *regionbase,
int region_type,
UI: Region polling support Introduces *ARegionType.poll()* as a way to dynamically add/remove a region. The region is still there internally, but is not accessible to the user. Previously editors would to this manually, by either removing/adding regions altogether, or hiding them, unsetting their alignment (so no AZones are added I assume) and removing their event handlers. Polling makes this much simpler. We plan to use this in #102879. This patch refactors multiple editors to use region polling: - File Browser - Sequencer - Clip Editor - Preferences Notes: - Previously, editors would lazy-create some of the regions. Versioning is added here to ensure they are always there. Could be a separate patch. - Some editors reuse a region in different display modes, and so additional work needs to be done to reinit regions they become available or the mode changes. Typically `V2D_IS_INIT` is unset for that, which isn't great. Could be improved, but not a new issue. Behavior change: - When the Preferences are opened as a regular editor, the "execution" region in the preferences that displays the *Save Preferences* button would still be there, but empty with a scrollbar. This patch makes it disappear entirely. ## Implementation - Introduces `ARegionType.poll()` - Before a window is drawn, all contained regions have their poll checked, and the result is stored in a flag (`RGN_FLAG_POLL_FAILED` - runtime-only flag). - If the result of the poll changes, the area is re-initialized and event handlers are added/removed. - UI code checks the flag as needed. Pull Request: https://projects.blender.org/blender/blender/pulls/105088
2023-04-05 15:30:32 +02:00
const char *allocname,
int link_after_region_type)
{
2021-06-18 14:27:43 +10:00
ARegion *link_after_region = nullptr;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == region_type) {
2021-06-18 14:27:43 +10:00
return nullptr;
}
if (region->regiontype == link_after_region_type) {
link_after_region = region;
}
}
UI: Region polling support Introduces *ARegionType.poll()* as a way to dynamically add/remove a region. The region is still there internally, but is not accessible to the user. Previously editors would to this manually, by either removing/adding regions altogether, or hiding them, unsetting their alignment (so no AZones are added I assume) and removing their event handlers. Polling makes this much simpler. We plan to use this in #102879. This patch refactors multiple editors to use region polling: - File Browser - Sequencer - Clip Editor - Preferences Notes: - Previously, editors would lazy-create some of the regions. Versioning is added here to ensure they are always there. Could be a separate patch. - Some editors reuse a region in different display modes, and so additional work needs to be done to reinit regions they become available or the mode changes. Typically `V2D_IS_INIT` is unset for that, which isn't great. Could be improved, but not a new issue. Behavior change: - When the Preferences are opened as a regular editor, the "execution" region in the preferences that displays the *Save Preferences* button would still be there, but empty with a scrollbar. This patch makes it disappear entirely. ## Implementation - Introduces `ARegionType.poll()` - Before a window is drawn, all contained regions have their poll checked, and the result is stored in a flag (`RGN_FLAG_POLL_FAILED` - runtime-only flag). - If the result of the poll changes, the area is re-initialized and event handlers are added/removed. - UI code checks the flag as needed. Pull Request: https://projects.blender.org/blender/blender/pulls/105088
2023-04-05 15:30:32 +02:00
ARegion *new_region = static_cast<ARegion *>(MEM_callocN(sizeof(ARegion), allocname));
new_region->regiontype = region_type;
BLI_insertlinkafter(regionbase, link_after_region, new_region);
return new_region;
}
ARegion *do_versions_ensure_region(ListBase *regionbase,
int region_type,
const char *allocname,
int link_after_region_type)
{
ARegion *link_after_region = nullptr;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == region_type) {
return region;
}
if (region->regiontype == link_after_region_type) {
link_after_region = region;
}
}
ARegion *new_region = MEM_cnew<ARegion>(allocname);
new_region->regiontype = region_type;
BLI_insertlinkafter(regionbase, link_after_region, new_region);
return new_region;
}
ID *do_versions_rename_id(Main *bmain,
const short id_type,
const char *name_src,
const char *name_dst)
{
/* We can ignore libraries */
ListBase *lb = which_libbase(bmain, id_type);
ID *id = nullptr;
LISTBASE_FOREACH (ID *, idtest, lb) {
if (!ID_IS_LINKED(idtest)) {
if (STREQ(idtest->name + 2, name_src)) {
id = idtest;
}
if (STREQ(idtest->name + 2, name_dst)) {
return nullptr;
}
}
}
if (id != nullptr) {
BKE_main_namemap_remove_name(bmain, id, id->name + 2);
BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2);
/* We know it's unique, this just sorts. */
BLI_libblock_ensure_unique_name(bmain, id->name);
}
return id;
}
static void change_node_socket_name(ListBase *sockets, const char *old_name, const char *new_name)
{
LISTBASE_FOREACH (bNodeSocket *, socket, sockets) {
if (STREQ(socket->name, old_name)) {
2023-05-09 12:50:37 +10:00
STRNCPY(socket->name, new_name);
}
if (STREQ(socket->identifier, old_name)) {
2023-05-09 12:50:37 +10:00
STRNCPY(socket->identifier, new_name);
}
}
}
bool version_node_socket_is_used(bNodeSocket *sock)
{
return sock->flag & SOCK_IS_LINKED;
}
void version_node_socket_id_delim(bNodeSocket *socket)
{
StringRef name = socket->name;
StringRef id = socket->identifier;
if (!id.startswith(name)) {
/* We only need to affect the case where the identifier starts with the name. */
return;
}
StringRef id_number = id.drop_known_prefix(name);
if (id_number.is_empty()) {
/* The name was already unique, and didn't need numbers at the end for the id. */
return;
}
if (id_number.startswith(".")) {
socket->identifier[name.size()] = '_';
}
}
void version_node_socket_name(bNodeTree *ntree,
const int node_type,
const char *old_name,
const char *new_name)
{
for (bNode *node : ntree->all_nodes()) {
if (node->type == node_type) {
change_node_socket_name(&node->inputs, old_name, new_name);
change_node_socket_name(&node->outputs, old_name, new_name);
}
}
}
void version_node_input_socket_name(bNodeTree *ntree,
const int node_type,
const char *old_name,
const char *new_name)
{
for (bNode *node : ntree->all_nodes()) {
if (node->type == node_type) {
change_node_socket_name(&node->inputs, old_name, new_name);
}
}
}
void version_node_output_socket_name(bNodeTree *ntree,
2021-11-10 00:55:05 +11:00
const int node_type,
const char *old_name,
const char *new_name)
{
for (bNode *node : ntree->all_nodes()) {
if (node->type == node_type) {
change_node_socket_name(&node->outputs, old_name, new_name);
}
}
}
bNodeSocket *version_node_add_socket_if_not_exist(bNodeTree *ntree,
bNode *node,
int in_out,
int type,
int subtype,
const char *identifier,
const char *name)
{
bNodeSocket *sock = nodeFindSocket(node, eNodeSocketInOut(in_out), identifier);
if (sock != nullptr) {
return sock;
}
return nodeAddStaticSocket(
ntree, node, eNodeSocketInOut(in_out), type, subtype, identifier, name);
}
void version_node_id(bNodeTree *ntree, const int node_type, const char *new_name)
{
for (bNode *node : ntree->all_nodes()) {
if (node->type == node_type) {
if (!STREQ(node->idname, new_name)) {
2023-06-19 20:06:55 +10:00
STRNCPY(node->idname, new_name);
}
}
}
}
void version_node_socket_index_animdata(Main *bmain,
const int node_tree_type,
const int node_type,
const int socket_index_orig,
const int socket_index_offset,
const int total_number_of_sockets)
{
/* The for loop for the input ids is at the top level otherwise we lose the animation
* keyframe data. Not sure what causes that, so I (Sybren) moved the code here from
* versioning_290.cc as-is (structure-wise). */
for (int input_index = total_number_of_sockets - 1; input_index >= socket_index_orig;
input_index--)
{
FOREACH_NODETREE_BEGIN (bmain, ntree, owner_id) {
if (ntree->type != node_tree_type) {
continue;
}
for (bNode *node : ntree->all_nodes()) {
if (node->type != node_type) {
continue;
}
const size_t node_name_length = strlen(node->name);
const size_t node_name_escaped_max_length = (node_name_length * 2);
char *node_name_escaped = (char *)MEM_mallocN(node_name_escaped_max_length + 1,
"escaped name");
BLI_str_escape(node_name_escaped, node->name, node_name_escaped_max_length);
char *rna_path_prefix = BLI_sprintfN("nodes[\"%s\"].inputs", node_name_escaped);
const int new_index = input_index + socket_index_offset;
BKE_animdata_fix_paths_rename_all_ex(
2021-11-02 16:02:13 +11:00
bmain, owner_id, rna_path_prefix, nullptr, nullptr, input_index, new_index, false);
MEM_freeN(rna_path_prefix);
MEM_freeN(node_name_escaped);
}
}
FOREACH_NODETREE_END;
}
}
Nodes: refactor node tree update handling Goals of this refactor: * More unified approach to updating everything that needs to be updated after a change in a node tree. * The updates should happen in the correct order and quadratic or worse algorithms should be avoided. * Improve detection of changes to the output to avoid tagging the depsgraph when it's not necessary. * Move towards a more declarative style of defining nodes by having a more centralized update procedure. The refactor consists of two main parts: * Node tree tagging and update refactor. * Generally, when changes are done to a node tree, it is tagged dirty until a global update function is called that updates everything in the correct order. * The tagging is more fine-grained compared to before, to allow for more precise depsgraph update tagging. * Depsgraph changes. * The shading specific depsgraph node for node trees as been removed. * Instead, there is a new `NTREE_OUTPUT` depsgrap node, which is only tagged when the output of the node tree changed (e.g. the Group Output or Material Output node). * The copy-on-write relation from node trees to the data block they are embedded in is now non-flushing. This avoids e.g. triggering a material update after the shader node tree changed in unrelated ways. Instead the material has a flushing relation to the new `NTREE_OUTPUT` node now. * The depsgraph no longer reports data block changes through to cycles through `Depsgraph.updates` when only the node tree changed in ways that do not affect the output. Avoiding unnecessary updates seems to work well for geometry nodes and cycles. The situation is a bit worse when there are drivers on the node tree, but that could potentially be improved separately in the future. Avoiding updates in eevee and the compositor is more tricky, but also less urgent. * Eevee updates are triggered by calling `DRW_notify_view_update` in `ED_render_view3d_update` indirectly from `DEG_editors_update`. * Compositor updates are triggered by `ED_node_composite_job` in `node_area_refresh`. This is triggered by calling `ED_area_tag_refresh` in `node_area_listener`. Removing updates always has the risk of breaking some dependency that no one was aware of. It's not unlikely that this will happen here as well. Adding back missing updates should be quite a bit easier than getting rid of unnecessary updates though. Differential Revision: https://developer.blender.org/D13246
2021-12-21 15:18:56 +01:00
void version_socket_update_is_used(bNodeTree *ntree)
{
for (bNode *node : ntree->all_nodes()) {
Nodes: refactor node tree update handling Goals of this refactor: * More unified approach to updating everything that needs to be updated after a change in a node tree. * The updates should happen in the correct order and quadratic or worse algorithms should be avoided. * Improve detection of changes to the output to avoid tagging the depsgraph when it's not necessary. * Move towards a more declarative style of defining nodes by having a more centralized update procedure. The refactor consists of two main parts: * Node tree tagging and update refactor. * Generally, when changes are done to a node tree, it is tagged dirty until a global update function is called that updates everything in the correct order. * The tagging is more fine-grained compared to before, to allow for more precise depsgraph update tagging. * Depsgraph changes. * The shading specific depsgraph node for node trees as been removed. * Instead, there is a new `NTREE_OUTPUT` depsgrap node, which is only tagged when the output of the node tree changed (e.g. the Group Output or Material Output node). * The copy-on-write relation from node trees to the data block they are embedded in is now non-flushing. This avoids e.g. triggering a material update after the shader node tree changed in unrelated ways. Instead the material has a flushing relation to the new `NTREE_OUTPUT` node now. * The depsgraph no longer reports data block changes through to cycles through `Depsgraph.updates` when only the node tree changed in ways that do not affect the output. Avoiding unnecessary updates seems to work well for geometry nodes and cycles. The situation is a bit worse when there are drivers on the node tree, but that could potentially be improved separately in the future. Avoiding updates in eevee and the compositor is more tricky, but also less urgent. * Eevee updates are triggered by calling `DRW_notify_view_update` in `ED_render_view3d_update` indirectly from `DEG_editors_update`. * Compositor updates are triggered by `ED_node_composite_job` in `node_area_refresh`. This is triggered by calling `ED_area_tag_refresh` in `node_area_listener`. Removing updates always has the risk of breaking some dependency that no one was aware of. It's not unlikely that this will happen here as well. Adding back missing updates should be quite a bit easier than getting rid of unnecessary updates though. Differential Revision: https://developer.blender.org/D13246
2021-12-21 15:18:56 +01:00
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
socket->flag &= ~SOCK_IS_LINKED;
Nodes: refactor node tree update handling Goals of this refactor: * More unified approach to updating everything that needs to be updated after a change in a node tree. * The updates should happen in the correct order and quadratic or worse algorithms should be avoided. * Improve detection of changes to the output to avoid tagging the depsgraph when it's not necessary. * Move towards a more declarative style of defining nodes by having a more centralized update procedure. The refactor consists of two main parts: * Node tree tagging and update refactor. * Generally, when changes are done to a node tree, it is tagged dirty until a global update function is called that updates everything in the correct order. * The tagging is more fine-grained compared to before, to allow for more precise depsgraph update tagging. * Depsgraph changes. * The shading specific depsgraph node for node trees as been removed. * Instead, there is a new `NTREE_OUTPUT` depsgrap node, which is only tagged when the output of the node tree changed (e.g. the Group Output or Material Output node). * The copy-on-write relation from node trees to the data block they are embedded in is now non-flushing. This avoids e.g. triggering a material update after the shader node tree changed in unrelated ways. Instead the material has a flushing relation to the new `NTREE_OUTPUT` node now. * The depsgraph no longer reports data block changes through to cycles through `Depsgraph.updates` when only the node tree changed in ways that do not affect the output. Avoiding unnecessary updates seems to work well for geometry nodes and cycles. The situation is a bit worse when there are drivers on the node tree, but that could potentially be improved separately in the future. Avoiding updates in eevee and the compositor is more tricky, but also less urgent. * Eevee updates are triggered by calling `DRW_notify_view_update` in `ED_render_view3d_update` indirectly from `DEG_editors_update`. * Compositor updates are triggered by `ED_node_composite_job` in `node_area_refresh`. This is triggered by calling `ED_area_tag_refresh` in `node_area_listener`. Removing updates always has the risk of breaking some dependency that no one was aware of. It's not unlikely that this will happen here as well. Adding back missing updates should be quite a bit easier than getting rid of unnecessary updates though. Differential Revision: https://developer.blender.org/D13246
2021-12-21 15:18:56 +01:00
}
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
socket->flag &= ~SOCK_IS_LINKED;
Nodes: refactor node tree update handling Goals of this refactor: * More unified approach to updating everything that needs to be updated after a change in a node tree. * The updates should happen in the correct order and quadratic or worse algorithms should be avoided. * Improve detection of changes to the output to avoid tagging the depsgraph when it's not necessary. * Move towards a more declarative style of defining nodes by having a more centralized update procedure. The refactor consists of two main parts: * Node tree tagging and update refactor. * Generally, when changes are done to a node tree, it is tagged dirty until a global update function is called that updates everything in the correct order. * The tagging is more fine-grained compared to before, to allow for more precise depsgraph update tagging. * Depsgraph changes. * The shading specific depsgraph node for node trees as been removed. * Instead, there is a new `NTREE_OUTPUT` depsgrap node, which is only tagged when the output of the node tree changed (e.g. the Group Output or Material Output node). * The copy-on-write relation from node trees to the data block they are embedded in is now non-flushing. This avoids e.g. triggering a material update after the shader node tree changed in unrelated ways. Instead the material has a flushing relation to the new `NTREE_OUTPUT` node now. * The depsgraph no longer reports data block changes through to cycles through `Depsgraph.updates` when only the node tree changed in ways that do not affect the output. Avoiding unnecessary updates seems to work well for geometry nodes and cycles. The situation is a bit worse when there are drivers on the node tree, but that could potentially be improved separately in the future. Avoiding updates in eevee and the compositor is more tricky, but also less urgent. * Eevee updates are triggered by calling `DRW_notify_view_update` in `ED_render_view3d_update` indirectly from `DEG_editors_update`. * Compositor updates are triggered by `ED_node_composite_job` in `node_area_refresh`. This is triggered by calling `ED_area_tag_refresh` in `node_area_listener`. Removing updates always has the risk of breaking some dependency that no one was aware of. It's not unlikely that this will happen here as well. Adding back missing updates should be quite a bit easier than getting rid of unnecessary updates though. Differential Revision: https://developer.blender.org/D13246
2021-12-21 15:18:56 +01:00
}
}
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
link->fromsock->flag |= SOCK_IS_LINKED;
link->tosock->flag |= SOCK_IS_LINKED;
Nodes: refactor node tree update handling Goals of this refactor: * More unified approach to updating everything that needs to be updated after a change in a node tree. * The updates should happen in the correct order and quadratic or worse algorithms should be avoided. * Improve detection of changes to the output to avoid tagging the depsgraph when it's not necessary. * Move towards a more declarative style of defining nodes by having a more centralized update procedure. The refactor consists of two main parts: * Node tree tagging and update refactor. * Generally, when changes are done to a node tree, it is tagged dirty until a global update function is called that updates everything in the correct order. * The tagging is more fine-grained compared to before, to allow for more precise depsgraph update tagging. * Depsgraph changes. * The shading specific depsgraph node for node trees as been removed. * Instead, there is a new `NTREE_OUTPUT` depsgrap node, which is only tagged when the output of the node tree changed (e.g. the Group Output or Material Output node). * The copy-on-write relation from node trees to the data block they are embedded in is now non-flushing. This avoids e.g. triggering a material update after the shader node tree changed in unrelated ways. Instead the material has a flushing relation to the new `NTREE_OUTPUT` node now. * The depsgraph no longer reports data block changes through to cycles through `Depsgraph.updates` when only the node tree changed in ways that do not affect the output. Avoiding unnecessary updates seems to work well for geometry nodes and cycles. The situation is a bit worse when there are drivers on the node tree, but that could potentially be improved separately in the future. Avoiding updates in eevee and the compositor is more tricky, but also less urgent. * Eevee updates are triggered by calling `DRW_notify_view_update` in `ED_render_view3d_update` indirectly from `DEG_editors_update`. * Compositor updates are triggered by `ED_node_composite_job` in `node_area_refresh`. This is triggered by calling `ED_area_tag_refresh` in `node_area_listener`. Removing updates always has the risk of breaking some dependency that no one was aware of. It's not unlikely that this will happen here as well. Adding back missing updates should be quite a bit easier than getting rid of unnecessary updates though. Differential Revision: https://developer.blender.org/D13246
2021-12-21 15:18:56 +01:00
}
}
ARegion *do_versions_add_region(int regiontype, const char *name)
{
ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), name);
region->regiontype = regiontype;
return region;
}
void node_tree_relink_with_socket_id_map(bNodeTree &ntree,
bNode &old_node,
bNode &new_node,
const Map<std::string, std::string> &map)
{
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
if (link->tonode == &old_node) {
bNodeSocket *old_socket = link->tosock;
if (const std::string *new_identifier = map.lookup_ptr_as(old_socket->identifier)) {
bNodeSocket *new_socket = nodeFindSocket(&new_node, SOCK_IN, new_identifier->c_str());
link->tonode = &new_node;
link->tosock = new_socket;
old_socket->link = nullptr;
}
}
if (link->fromnode == &old_node) {
bNodeSocket *old_socket = link->fromsock;
if (const std::string *new_identifier = map.lookup_ptr_as(old_socket->identifier)) {
bNodeSocket *new_socket = nodeFindSocket(&new_node, SOCK_OUT, new_identifier->c_str());
link->fromnode = &new_node;
link->fromsock = new_socket;
old_socket->link = nullptr;
}
}
}
}
static blender::Vector<bNodeLink *> find_connected_links(bNodeTree *ntree, bNodeSocket *in_socket)
{
blender::Vector<bNodeLink *> links;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (link->tosock == in_socket) {
links.append(link);
}
}
return links;
}
void add_realize_instances_before_socket(bNodeTree *ntree,
bNode *node,
bNodeSocket *geometry_socket)
{
BLI_assert(geometry_socket->type == SOCK_GEOMETRY);
blender::Vector<bNodeLink *> links = find_connected_links(ntree, geometry_socket);
for (bNodeLink *link : links) {
/* If the realize instances node is already before this socket, no need to continue. */
if (link->fromnode->type == GEO_NODE_REALIZE_INSTANCES) {
return;
}
bNode *realize_node = nodeAddStaticNode(nullptr, ntree, GEO_NODE_REALIZE_INSTANCES);
realize_node->parent = node->parent;
realize_node->locx = node->locx - 100;
realize_node->locy = node->locy;
nodeAddLink(ntree,
link->fromnode,
link->fromsock,
realize_node,
static_cast<bNodeSocket *>(realize_node->inputs.first));
link->fromnode = realize_node;
link->fromsock = static_cast<bNodeSocket *>(realize_node->outputs.first);
}
}
float *version_cycles_node_socket_float_value(bNodeSocket *socket)
{
bNodeSocketValueFloat *socket_data = static_cast<bNodeSocketValueFloat *>(socket->default_value);
return &socket_data->value;
}
float *version_cycles_node_socket_rgba_value(bNodeSocket *socket)
{
bNodeSocketValueRGBA *socket_data = static_cast<bNodeSocketValueRGBA *>(socket->default_value);
return socket_data->value;
}
float *version_cycles_node_socket_vector_value(bNodeSocket *socket)
{
bNodeSocketValueVector *socket_data = static_cast<bNodeSocketValueVector *>(
socket->default_value);
return socket_data->value;
}
IDProperty *version_cycles_properties_from_ID(ID *id)
{
IDProperty *idprop = IDP_GetProperties(id);
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : nullptr;
}
IDProperty *version_cycles_properties_from_view_layer(ViewLayer *view_layer)
{
IDProperty *idprop = view_layer->id_properties;
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : nullptr;
}
float version_cycles_property_float(IDProperty *idprop, const char *name, float default_value)
{
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_FLOAT);
return (prop) ? IDP_Float(prop) : default_value;
}
int version_cycles_property_int(IDProperty *idprop, const char *name, int default_value)
{
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT);
return (prop) ? IDP_Int(prop) : default_value;
}
void version_cycles_property_int_set(IDProperty *idprop, const char *name, int value)
{
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT);
if (prop) {
IDP_Int(prop) = value;
}
else {
IDPropertyTemplate val = {0};
val.i = value;
IDP_AddToGroup(idprop, IDP_New(IDP_INT, &val, name));
}
}
bool version_cycles_property_boolean(IDProperty *idprop, const char *name, bool default_value)
{
return version_cycles_property_int(idprop, name, default_value);
}
void version_cycles_property_boolean_set(IDProperty *idprop, const char *name, bool value)
{
version_cycles_property_int_set(idprop, name, value);
}
IDProperty *version_cycles_visibility_properties_from_ID(ID *id)
{
IDProperty *idprop = IDP_GetProperties(id);
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles_visibility", IDP_GROUP) : nullptr;
}
void version_update_node_input(
bNodeTree *ntree,
FunctionRef<bool(bNode *)> check_node,
const char *socket_identifier,
FunctionRef<void(bNode *, bNodeSocket *)> update_input,
FunctionRef<void(bNode *, bNodeSocket *, bNode *, bNodeSocket *)> update_input_link)
{
bool need_update = false;
/* Iterate backwards from end so we don't encounter newly added links. */
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &ntree->links) {
/* Detect link to replace. */
bNode *fromnode = link->fromnode;
bNodeSocket *fromsock = link->fromsock;
bNode *tonode = link->tonode;
bNodeSocket *tosock = link->tosock;
if (!(tonode != nullptr && check_node(tonode) && STREQ(tosock->identifier, socket_identifier)))
{
continue;
}
/* Replace links with updated equivalent */
nodeRemLink(ntree, link);
update_input_link(fromnode, fromsock, tonode, tosock);
need_update = true;
}
/* Update sockets and/or their default values.
* Do this after the link update in case it changes the identifier. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (check_node(node)) {
bNodeSocket *input = nodeFindSocket(node, SOCK_IN, socket_identifier);
if (input != nullptr) {
update_input(node, input);
}
}
}
if (need_update) {
version_socket_update_is_used(ntree);
}
}
static bool blendfile_or_libraries_versions_atleast(Main *bmain,
const short versionfile,
const short subversionfile)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, versionfile, subversionfile)) {
return false;
}
LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
if (!MAIN_VERSION_FILE_ATLEAST(library, versionfile, subversionfile)) {
return false;
}
}
return true;
}
void do_versions_after_setup(Main *new_bmain, BlendFileReadReport *reports)
{
/* WARNING: The code below may add IDs. These IDs _will_ be (by definition) conforming to current
* code's version already, and _must not_ be 'versionned' again.
*
* This means that when adding code here, _extreme_ care must be taken that it will not badly
* affect these 'modern' IDs potentially added by already existing processing.
*
* Adding code here should only be done in exceptional cases.
*
* Some further points to keep in mind:
* - While typically versioning order should be respected in code below (i.e. versioning
* affecting older versions should be done first), _this is not a hard rule_. And it should
* not be assumed older code must not be checked when adding newer code.
* - Do not rely strongly on versioning numbers here. This code may be run on data from
* different Blender versions (through the usage of linked data), and all existing data have
* already been processed through the whole do_version during blendfile reading itself. So
* decision to apply some versioning on some data should mostly rely on the data itself.
* - Unlike the regular do_version code, this one should _not_ be assumed as 'valid forever'.
* It is closer to the Editing or BKE code in that respect, changes to the logic or data
2023-08-18 08:51:29 +10:00
* model of an ID will require a careful update here as well.
*
* Another critical weakness of this code is that it is currently _not_ performed on data linked
* during an editing session, but only on data linked while reading a whole blendfile. This will
* have to be fixed at some point.
*/
/* NOTE: Version number is checked against Main version (i.e. current blend file version), AND
* the versions of all the linked libraries. */
if (!blendfile_or_libraries_versions_atleast(new_bmain, 250, 0)) {
do_versions_ipos_to_animato(new_bmain);
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 250, 0)) {
LISTBASE_FOREACH (Scene *, scene, &new_bmain->scenes) {
if (scene->ed) {
SEQ_doversion_250_sound_proxy_update(new_bmain, scene->ed);
}
}
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 302, 1)) {
BKE_lib_override_library_main_proxy_convert(new_bmain, reports);
/* Currently liboverride code can generate invalid namemap. This is a known issue, requires
* #107847 to be properly fixed. */
BKE_main_namemap_validate_and_fix(new_bmain);
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 302, 3)) {
/* Does not add any new IDs, but needs the full Main data-base. */
BKE_lib_override_library_main_hierarchy_root_ensure(new_bmain);
}
Mesh: Replace auto smooth with node group Design task: #93551 This PR replaces the auto smooth option with a geometry nodes modifier that sets the sharp edge attribute. This solves a fair number of long- standing problems related to auto smooth, simplifies the process of normal computation, and allows Blender to automatically choose between face, vertex, and face corner normals based on the sharp edge and face attributes. Versioning adds a geometry node group to objects with meshes that had auto-smooth enabled. The modifier can be applied, which also improves performance. Auto smooth is now unnecessary to get a combination of sharp and smooth edges. In general workflows are changed a bit. Separate procedural and destructive workflows are available. Custom normals can be used immediately without turning on the removed auto smooth option. **Procedural** The node group asset "Smooth by Angle" is the main way to set sharp normals based on the edge angle. It can be accessed directly in the add modifier menu. Of course the modifier can be reordered, muted, or applied like any other, or changed internally like any geometry nodes modifier. **Destructive** Often the sharp edges don't need to be dynamic. This can give better performance since edge angles don't need to be recalculated. In edit mode the two operators "Select Sharp Edges" and "Mark Sharp" can be used. In other modes, the "Shade Smooth by Angle" controls the edge sharpness directly. ### Breaking API Changes - `use_auto_smooth` is removed. Face corner normals are now used automatically if there are mixed smooth vs. not smooth tags. Meshes now always use custom normals if they exist. - In Cycles, the lack of the separate auto smooth state makes normals look triangulated when all faces are shaded smooth. - `auto_smooth_angle` is removed. Replaced by a modifier (or operator) controlling the sharp edge attribute. This means the mesh itself (without an object) doesn't know anything about automatically smoothing by angle anymore. - `create_normals_split`, `calc_normals_split`, and `free_normals_split` are removed, and are replaced by the simpler `Mesh.corner_normals` collection property. Since it gives access to the normals cache, it is automatically updated when relevant data changes. Addons are updated here: https://projects.blender.org/blender/blender-addons/pulls/104609 ### Tests - `geo_node_curves_test_deform_curves_on_surface` has slightly different results because face corner normals are used instead of interpolated vertex normals. - `bf_wavefront_obj_tests` has different export results for one file which mixed sharp and smooth faces without turning on auto smooth. - `cycles_mesh_cpu` has one object which is completely flat shaded. Previously every edge was split before rendering, now it looks triangulated. Pull Request: https://projects.blender.org/blender/blender/pulls/108014
2023-10-20 16:54:08 +02:00
if (!blendfile_or_libraries_versions_atleast(new_bmain, 401, 2)) {
BKE_main_mesh_legacy_convert_auto_smooth(*new_bmain);
}
}