Files
test2/source/blender/modifiers/intern/MOD_nodes.cc

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

3107 lines
115 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2005 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
/** \file
* \ingroup modifiers
*/
#include <cstring>
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
#include <fmt/format.h>
#include <sstream>
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include <string>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
Spreadsheet: breadcrumbs and node pinning This introduces a context path to the spreadsheet editor, which contains information about what data is shown in the spreadsheet. The context path (breadcrumbs) can reference a specific node in a node group hierarchy. During object evaluation, the geometry nodes modifier checks what data is currently requested by visible spreadsheets and stores the corresponding geometry sets separately for later access. The context path can be updated by the user explicitely, by clicking on the new icon in the header of nodes. Under some circumstances, the context path is updated automatically based on Blender's context. This patch also consolidates the "Node" and "Final" object evaluation mode to just "Evaluated". Based on the current context path, either the final geometry set of an object will be displayed, or the data at a specific node. The new preview icon in geometry nodes now behaves more like a toggle. It can be clicked again to clear the context path in an open spreadsheet editor. Previously, only an object could be pinned in the spreadsheet editor. Now it is possible to pin the entire context path. That allows two different spreadsheets to display geometry data from two different nodes. The breadcrumbs in the spreadsheet header can be collapsed by clicking on the arrow icons. It's not ideal but works well for now. This might be changed again, if we get a data set region on the left. Differential Revision: https://developer.blender.org/D10931
2021-04-15 08:57:10 +02:00
#include "BLI_multi_value_map.hh"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include "BLI_set.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
#include "DNA_array_utils.hh"
#include "DNA_collection_types.h"
Curves: support deforming curves on surface Curves that are attached to a surface can now follow the surface when it is modified using shape keys or modifiers (but not when the original surface is deformed in edit or sculpt mode). The surface is allowed to be changed in any way that keeps uv maps intact. So deformation is allowed, but also some topology changes like subdivision. The following features are added: * A new `Deform Curves on Surface` node, which deforms curves with attachment information based on the surface object and uv map set in the properties panel. * A new `Add Rest Position` checkbox in the shape keys panel. When checked, a new `rest_position` vector attribute is added to the mesh before shape keys and modifiers are applied. This is necessary to support proper deformation of the curves, but can also be used for other purposes. * The `Add > Curve > Empty Hair` operator now sets up a simple geometry nodes setup that deforms the hair. It also makes sure that the rest position attribute is added to the surface. * A new `Object (Attach Curves to Surface)` operator in the `Set Parent To` (ctrl+P) menu, which attaches existing curves to the surface and sets the surface object as parent. Limitations: * Sculpting the procedurally deformed curves will be implemented separately. * The `Deform Curves on Surface` node is not generic and can only be used for one specific purpose currently. We plan to generalize this more in the future by adding support by exposing more inputs and/or by turning it into a node group. Differential Revision: https://developer.blender.org/D14864
2022-07-08 14:45:48 +02:00
#include "DNA_curves_types.h"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include "DNA_defaults.h"
#include "DNA_material_types.h"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
Geometry Nodes: viewport preview This adds support for showing geometry passed to the Viewer in the 3d viewport (instead of just in the spreadsheet). The "viewer geometry" bypasses the group output. So it is not necessary to change the final output of the node group to be able to see the intermediate geometry. **Activation and deactivation of a viewer node** * A viewer node is activated by clicking on it. * Ctrl+shift+click on any node/socket connects it to the viewer and makes it active. * Ctrl+shift+click in empty space deactivates the active viewer. * When the active viewer is not visible anymore (e.g. another object is selected, or the current node group is exit), it is deactivated. * Clicking on the icon in the header of the Viewer node toggles whether its active or not. **Pinning** * The spreadsheet still allows pinning the active viewer as before. When pinned, the spreadsheet still references the viewer node even when it becomes inactive. * The viewport does not support pinning at the moment. It always shows the active viewer. **Attribute** * When a field is linked to the second input of the viewer node it is displayed as an overlay in the viewport. * When possible the correct domain for the attribute is determined automatically. This does not work in all cases. It falls back to the face corner domain on meshes and the point domain on curves. When necessary, the domain can be picked manually. * The spreadsheet now only shows the "Viewer" column for the domain that is selected in the Viewer node. * Instance attributes are visualized as a constant color per instance. **Viewport Options** * The attribute overlay opacity can be controlled with the "Viewer Node" setting in the overlays popover. * A viewport can be configured not to show intermediate viewer-geometry by disabling the "Viewer Node" option in the "View" menu. **Implementation Details** * The "spreadsheet context path" was generalized to a "viewer path" that is used in more places now. * The viewer node itself determines the attribute domain, evaluates the field and stores the result in a `.viewer` attribute. * A new "viewer attribute' overlay displays the data from the `.viewer` attribute. * The ground truth for the active viewer node is stored in the workspace now. Node editors, spreadsheets and viewports retrieve the active viewer from there unless they are pinned. * The depsgraph object iterator has a new "viewer path" setting. When set, the viewed geometry of the corresponding object is part of the iterator instead of the final evaluated geometry. * To support the instance attribute overlay `DupliObject` was extended to contain the information necessary for drawing the overlay. * The ctrl+shift+click operator has been refactored so that it can make existing links to viewers active again. * The auto-domain-detection in the Viewer node works by checking the "preferred domain" for every field input. If there is not exactly one preferred domain, the fallback is used. Known limitations: * Loose edges of meshes don't have the attribute overlay. This could be added separately if necessary. * Some attributes are hard to visualize as a color directly. For example, the values might have to be normalized or some should be drawn as arrays. For now, we encourage users to build node groups that generate appropriate viewer-geometry. We might include some of that functionality in future versions. Support for displaying attribute values as text in the viewport is planned as well. * There seems to be an issue with the attribute overlay for pointclouds on nvidia gpus, to be investigated. Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
#include "DNA_view3d_types.h"
#include "DNA_windowmanager_types.h"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
#include "BKE_bake_data_block_map.hh"
#include "BKE_bake_geometry_nodes_modifier.hh"
#include "BKE_compute_context_cache.hh"
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
#include "BKE_compute_contexts.hh"
#include "BKE_customdata.hh"
#include "BKE_global.hh"
2024-03-26 12:57:30 -04:00
#include "BKE_idprop.hh"
2024-01-15 12:44:04 -05:00
#include "BKE_lib_id.hh"
#include "BKE_lib_query.hh"
#include "BKE_main.hh"
#include "BKE_mesh.hh"
2023-11-14 09:30:40 +01:00
#include "BKE_modifier.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.hh"
#include "BKE_object.hh"
#include "BKE_packedFile.hh"
#include "BKE_pointcloud.hh"
#include "BKE_screen.hh"
2024-04-12 17:03:18 -04:00
#include "BKE_workspace.hh"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include "BLO_read_write.hh"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include "UI_interface.hh"
#include "UI_resources.hh"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include "BLT_translation.hh"
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
#include "WM_api.hh"
#include "WM_types.hh"
#include "RNA_access.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_query.hh"
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
#include "DEG_depsgraph_writeback_sync.hh"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include "MOD_modifiertypes.hh"
#include "MOD_nodes.hh"
#include "MOD_ui_common.hh"
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
#include "ED_node.hh"
#include "ED_object.hh"
#include "ED_screen.hh"
#include "ED_undo.hh"
Geometry Nodes: viewport preview This adds support for showing geometry passed to the Viewer in the 3d viewport (instead of just in the spreadsheet). The "viewer geometry" bypasses the group output. So it is not necessary to change the final output of the node group to be able to see the intermediate geometry. **Activation and deactivation of a viewer node** * A viewer node is activated by clicking on it. * Ctrl+shift+click on any node/socket connects it to the viewer and makes it active. * Ctrl+shift+click in empty space deactivates the active viewer. * When the active viewer is not visible anymore (e.g. another object is selected, or the current node group is exit), it is deactivated. * Clicking on the icon in the header of the Viewer node toggles whether its active or not. **Pinning** * The spreadsheet still allows pinning the active viewer as before. When pinned, the spreadsheet still references the viewer node even when it becomes inactive. * The viewport does not support pinning at the moment. It always shows the active viewer. **Attribute** * When a field is linked to the second input of the viewer node it is displayed as an overlay in the viewport. * When possible the correct domain for the attribute is determined automatically. This does not work in all cases. It falls back to the face corner domain on meshes and the point domain on curves. When necessary, the domain can be picked manually. * The spreadsheet now only shows the "Viewer" column for the domain that is selected in the Viewer node. * Instance attributes are visualized as a constant color per instance. **Viewport Options** * The attribute overlay opacity can be controlled with the "Viewer Node" setting in the overlays popover. * A viewport can be configured not to show intermediate viewer-geometry by disabling the "Viewer Node" option in the "View" menu. **Implementation Details** * The "spreadsheet context path" was generalized to a "viewer path" that is used in more places now. * The viewer node itself determines the attribute domain, evaluates the field and stores the result in a `.viewer` attribute. * A new "viewer attribute' overlay displays the data from the `.viewer` attribute. * The ground truth for the active viewer node is stored in the workspace now. Node editors, spreadsheets and viewports retrieve the active viewer from there unless they are pinned. * The depsgraph object iterator has a new "viewer path" setting. When set, the viewed geometry of the corresponding object is part of the iterator instead of the final evaluated geometry. * To support the instance attribute overlay `DupliObject` was extended to contain the information necessary for drawing the overlay. * The ctrl+shift+click operator has been refactored so that it can make existing links to viewers active again. * The auto-domain-detection in the Viewer node works by checking the "preferred domain" for every field input. If there is not exactly one preferred domain, the fallback is used. Known limitations: * Loose edges of meshes don't have the attribute overlay. This could be added separately if necessary. * Some attributes are hard to visualize as a color directly. For example, the values might have to be normalized or some should be drawn as arrays. For now, we encourage users to build node groups that generate appropriate viewer-geometry. We might include some of that functionality in future versions. Support for displaying attribute values as text in the viewport is planned as well. * There seems to be an issue with the attribute overlay for pointclouds on nvidia gpus, to be investigated. Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
#include "ED_viewer_path.hh"
Spreadsheet: breadcrumbs and node pinning This introduces a context path to the spreadsheet editor, which contains information about what data is shown in the spreadsheet. The context path (breadcrumbs) can reference a specific node in a node group hierarchy. During object evaluation, the geometry nodes modifier checks what data is currently requested by visible spreadsheets and stores the corresponding geometry sets separately for later access. The context path can be updated by the user explicitely, by clicking on the new icon in the header of nodes. Under some circumstances, the context path is updated automatically based on Blender's context. This patch also consolidates the "Node" and "Final" object evaluation mode to just "Evaluated". Based on the current context path, either the final geometry set of an object will be displayed, or the data at a specific node. The new preview icon in geometry nodes now behaves more like a toggle. It can be clicked again to clear the context path in an open spreadsheet editor. Previously, only an object could be pinned in the spreadsheet editor. Now it is possible to pin the entire context path. That allows two different spreadsheets to display geometry data from two different nodes. The breadcrumbs in the spreadsheet header can be collapsed by clicking on the arrow icons. It's not ideal but works well for now. This might be changed again, if we get a data set region on the left. Differential Revision: https://developer.blender.org/D10931
2021-04-15 08:57:10 +02:00
#include "NOD_geometry.hh"
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
#include "NOD_geometry_nodes_dependencies.hh"
#include "NOD_geometry_nodes_execute.hh"
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
#include "NOD_geometry_nodes_gizmos.hh"
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_node_declaration.hh"
Nodes: automatically gray out input values that don't affect the output This patch automatically grays out input values which can't affect the output currently. It works with inputs of group nodes, geometry nodes modifiers and node tools. To achieve this, it analyses the node tree and partially evaluates it to figure out which group inputs are currently not linked to an output or are disabled by e.g. some switch node. Original proposal: https://devtalk.blender.org/t/dynamic-socket-visibility/31874 Related info in blog post: https://code.blender.org/2023/11/geometry-nodes-workshop-november-2023/#dynamic-socket-visibility Follow up task for designing a UI that allows hiding sockets: #132706 Limitations: * The inferencing does not update correctly when a socket starts being animated/driven. I haven't found a good way to invalidate the cache in a good way reliably yet. It's only a very short term problem though. It fixes itself after the next modification of the node tree and is only noticeable when animating some specific sockets such as the switch node condition. * Whether a socket is grayed out is not exposed in the Python API yet. That will be done separately. * Only a partial evaluation is done to determine if an input affects an output. There should be no cases where a socket is found to be unused when it can actually affect the output. However, there can be cases where a socket is inferenced to be used even if it is not due to some complex condition. Depending on the exact circumstances, this can either be improved or the condition in the node tree should be simplified. Pull Request: https://projects.blender.org/blender/blender/pulls/132219
2025-01-21 12:53:24 +01:00
#include "NOD_socket_usage_inference.hh"
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
namespace lf = blender::fn::lazy_function;
namespace geo_log = blender::nodes::geo_eval_log;
namespace bake = blender::bke::bake;
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
namespace blender {
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
static void init_data(ModifierData *md)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
NodesModifierData *nmd = (NodesModifierData *)md;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(nmd, modifier));
nmd->modifier.layout_panel_open_flag |= 1 << NODES_MODIFIER_PANEL_WARNINGS;
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
MEMCPY_STRUCT_AFTER(nmd, DNA_struct_default_get(NodesModifierData), modifier);
nmd->runtime = MEM_new<NodesModifierRuntime>(__func__);
nmd->runtime->cache = std::make_shared<bake::ModifierCache>();
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
static void find_dependencies_from_settings(const NodesModifierSettings &settings,
nodes::GeometryNodesEvalDependencies &deps)
{
IDP_foreach_property(settings.properties, IDP_TYPE_FILTER_ID, [&](IDProperty *property) {
if (ID *id = IDP_Id(property)) {
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
deps.add_generic_id_full(id);
}
});
}
/* We don't know exactly what attributes from the other object we will need. */
static const CustomData_MeshMasks dependency_data_mask{CD_MASK_PROP_ALL | CD_MASK_MDEFORMVERT,
CD_MASK_PROP_ALL,
CD_MASK_PROP_ALL,
CD_MASK_PROP_ALL,
CD_MASK_PROP_ALL};
static void add_collection_relation(const ModifierUpdateDepsgraphContext *ctx,
Collection &collection)
{
DEG_add_collection_geometry_relation(ctx->node, &collection, "Nodes Modifier");
DEG_add_collection_geometry_customdata_mask(ctx->node, &collection, &dependency_data_mask);
}
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
static void add_object_relation(
const ModifierUpdateDepsgraphContext *ctx,
Object &object,
const nodes::GeometryNodesEvalDependencies::ObjectDependencyInfo &info)
{
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
if (info.transform) {
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_TRANSFORM, "Nodes Modifier");
}
if (&(ID &)object == &ctx->object->id) {
return;
}
if (info.geometry) {
if (object.type == OB_EMPTY && object.instance_collection != nullptr) {
add_collection_relation(ctx, *object.instance_collection);
}
else if (DEG_object_has_geometry_component(&object)) {
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier");
DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask);
}
}
if (object.type == OB_CAMERA) {
if (info.camera_parameters) {
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_PARAMETERS, "Nodes Modifier");
}
}
}
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->node_group == nullptr) {
return;
}
if (ID_MISSING(nmd->node_group)) {
return;
}
DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Nodes Modifier");
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
nodes::GeometryNodesEvalDependencies eval_deps =
nodes::gather_geometry_nodes_eval_dependencies_recursive(*nmd->node_group);
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
/* Create dependencies to data-blocks referenced by the settings in the modifier. */
find_dependencies_from_settings(nmd->settings, eval_deps);
Curves: support deforming curves on surface Curves that are attached to a surface can now follow the surface when it is modified using shape keys or modifiers (but not when the original surface is deformed in edit or sculpt mode). The surface is allowed to be changed in any way that keeps uv maps intact. So deformation is allowed, but also some topology changes like subdivision. The following features are added: * A new `Deform Curves on Surface` node, which deforms curves with attachment information based on the surface object and uv map set in the properties panel. * A new `Add Rest Position` checkbox in the shape keys panel. When checked, a new `rest_position` vector attribute is added to the mesh before shape keys and modifiers are applied. This is necessary to support proper deformation of the curves, but can also be used for other purposes. * The `Add > Curve > Empty Hair` operator now sets up a simple geometry nodes setup that deforms the hair. It also makes sure that the rest position attribute is added to the surface. * A new `Object (Attach Curves to Surface)` operator in the `Set Parent To` (ctrl+P) menu, which attaches existing curves to the surface and sets the surface object as parent. Limitations: * Sculpting the procedurally deformed curves will be implemented separately. * The `Deform Curves on Surface` node is not generic and can only be used for one specific purpose currently. We plan to generalize this more in the future by adding support by exposing more inputs and/or by turning it into a node group. Differential Revision: https://developer.blender.org/D14864
2022-07-08 14:45:48 +02:00
if (ctx->object->type == OB_CURVES) {
Curves *curves_id = static_cast<Curves *>(ctx->object->data);
if (curves_id->surface != nullptr) {
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
eval_deps.add_object(curves_id->surface);
Curves: support deforming curves on surface Curves that are attached to a surface can now follow the surface when it is modified using shape keys or modifiers (but not when the original surface is deformed in edit or sculpt mode). The surface is allowed to be changed in any way that keeps uv maps intact. So deformation is allowed, but also some topology changes like subdivision. The following features are added: * A new `Deform Curves on Surface` node, which deforms curves with attachment information based on the surface object and uv map set in the properties panel. * A new `Add Rest Position` checkbox in the shape keys panel. When checked, a new `rest_position` vector attribute is added to the mesh before shape keys and modifiers are applied. This is necessary to support proper deformation of the curves, but can also be used for other purposes. * The `Add > Curve > Empty Hair` operator now sets up a simple geometry nodes setup that deforms the hair. It also makes sure that the rest position attribute is added to the surface. * A new `Object (Attach Curves to Surface)` operator in the `Set Parent To` (ctrl+P) menu, which attaches existing curves to the surface and sets the surface object as parent. Limitations: * Sculpting the procedurally deformed curves will be implemented separately. * The `Deform Curves on Surface` node is not generic and can only be used for one specific purpose currently. We plan to generalize this more in the future by adding support by exposing more inputs and/or by turning it into a node group. Differential Revision: https://developer.blender.org/D14864
2022-07-08 14:45:48 +02:00
}
}
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
for (const NodesModifierDataBlock &data_block : Span(bake.data_blocks, bake.data_blocks_num)) {
if (data_block.id) {
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
eval_deps.add_generic_id_full(data_block.id);
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
}
}
}
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
for (ID *id : eval_deps.ids.values()) {
switch ((ID_Type)GS(id->name)) {
case ID_OB: {
Object *object = reinterpret_cast<Object *>(id);
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
add_object_relation(
ctx, *object, eval_deps.objects_info.lookup_default(object->id.session_uid, {}));
break;
}
case ID_GR: {
Collection *collection = reinterpret_cast<Collection *>(id);
add_collection_relation(ctx, *collection);
break;
}
case ID_IM:
case ID_TE: {
DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier");
break;
}
default: {
/* Purposefully don't add relations for materials. While there are material sockets,
* the pointers are only passed around as handles rather than dereferenced. */
break;
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
}
}
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
if (eval_deps.needs_own_transform) {
DEG_add_depends_on_transform_relation(ctx->node, "Nodes Modifier");
}
Geometry Nodes: improve detecting data-block dependencies Previously, the data-block dependencies were always detected in `update_depsgraph` in `MOD_nodes.cc`. This would only be called when something called `DEG_relations_tag_update` before. We don't want to trigger a depsgraph rebuild after each operation in the node editor, as that would be expensive. However, that also meant that we often had to add data-block dependencies that are not actually used, but might be used if the user changed e.g. a link. A typical example for that is a object socket that has a default value, but the socket is also linked. Now, the dependencies referenced by the node tree are collected by the node tree update code which runs after all changes. This way we can detect whether the dependencies have changed. Only if they have changed, a depsgraph rebuild is triggered. This now allows also taking into account the mute status of nodes and whether an input is linked. There are still more things that could be taken into account. Most obviously whether a node is connected to an output. This can be done later. The most tricky aspect here is probably that we also have to consider all viewer nodes as output, because at the time the node runs, it's not known which viewer will actually be used (which depends on other editors). This also cleans up some special cases we had for e.g. the scene time node where we always had to trigger a depsgraph rebuild when it was added/removed because of its time dependence. This is now part of a more general system. This fixes #109219. Pull Request: https://projects.blender.org/blender/blender/pulls/131446
2024-12-05 18:02:14 +01:00
if (eval_deps.needs_active_camera) {
DEG_add_scene_camera_relation(ctx->node, ctx->scene, DEG_OB_COMP_TRANSFORM, "Nodes Modifier");
}
/* Active camera is a scene parameter that can change, so we need a relation for that, too. */
if (eval_deps.needs_active_camera || eval_deps.needs_scene_render_params) {
DEG_add_scene_relation(ctx->node, ctx->scene, DEG_SCENE_COMP_PARAMETERS, "Nodes Modifier");
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
static bool depends_on_time(Scene * /*scene*/, ModifierData *md)
{
const NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
const bNodeTree *tree = nmd->node_group;
if (tree == nullptr) {
return false;
}
if (ID_MISSING(tree)) {
return false;
}
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
if (bake.bake_mode == NODES_MODIFIER_BAKE_MODE_ANIMATION) {
return true;
}
}
nodes::GeometryNodesEvalDependencies eval_deps =
nodes::gather_geometry_nodes_eval_dependencies_recursive(*nmd->node_group);
return eval_deps.time_dependent;
}
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
walk(user_data, ob, (ID **)&nmd->node_group, IDWALK_CB_USER);
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
IDP_foreach_property(nmd->settings.properties, IDP_TYPE_FILTER_ID, [&](IDProperty *id_prop) {
walk(user_data, ob, (ID **)&id_prop->data.pointer, IDWALK_CB_USER);
});
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
for (NodesModifierDataBlock &data_block : MutableSpan(bake.data_blocks, bake.data_blocks_num))
{
walk(user_data, ob, &data_block.id, IDWALK_CB_USER);
}
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
static void foreach_tex_link(ModifierData *md, Object *ob, TexWalkFunc walk, void *user_data)
Geometry Nodes: show "Show Texture in texture tab" button This enables the quick access button [to show the relevant Texture in the Properties Editor] for textures used in geometry nodes. This goes in line to what we do for other textures: - modifier textures have this button - particle textures have this button - brush textures will soon have it, too (see D9813) When outside of the Properties Editor, the button will always show (if a texture is actually assigned), but will be inactive if no suiting Properties Editor to show the texture in can be found. Note this also changes the behavior to not show the button if _no_ texture is assigned (as in: we are still showing the "New" button). Previously it was always there (e.g. for modifier textures), even if it would take us to an empty texture tab. (Sure, we could add a texture there then, but imho it makes more sense to just start showing it once a texture is already there) For this to work with geometry nodes, the following chages were done: - implement foreachTexLink for geonode modifiers - new buttons_texture_user_node_property_add() that stores prop as well as node - also use NODE_ACTIVE_TEXTURE flag in geometry nodetrees notes: - this still uses the first suiting (as in: pinning does not interfere) Properties Editor it finds, this should (maybe?) find the _closest_ Property Editor instead (see related feedback in D9813). - this will already show the button for brush textures as well (disabled), but there is another mandatory change in an upcomming commit to make it work there as well (see D9813) ref. T85278 Maniphest Tasks: T85278 Differential Revision: https://developer.blender.org/D10293
2021-02-03 14:39:24 +01:00
{
PointerRNA ptr = RNA_pointer_create_discrete(&ob->id, &RNA_Modifier, md);
PropertyRNA *prop = RNA_struct_find_property(&ptr, "texture");
walk(user_data, ob, md, &ptr, prop);
Geometry Nodes: show "Show Texture in texture tab" button This enables the quick access button [to show the relevant Texture in the Properties Editor] for textures used in geometry nodes. This goes in line to what we do for other textures: - modifier textures have this button - particle textures have this button - brush textures will soon have it, too (see D9813) When outside of the Properties Editor, the button will always show (if a texture is actually assigned), but will be inactive if no suiting Properties Editor to show the texture in can be found. Note this also changes the behavior to not show the button if _no_ texture is assigned (as in: we are still showing the "New" button). Previously it was always there (e.g. for modifier textures), even if it would take us to an empty texture tab. (Sure, we could add a texture there then, but imho it makes more sense to just start showing it once a texture is already there) For this to work with geometry nodes, the following chages were done: - implement foreachTexLink for geonode modifiers - new buttons_texture_user_node_property_add() that stores prop as well as node - also use NODE_ACTIVE_TEXTURE flag in geometry nodetrees notes: - this still uses the first suiting (as in: pinning does not interfere) Properties Editor it finds, this should (maybe?) find the _closest_ Property Editor instead (see related feedback in D9813). - this will already show the button for brush textures as well (disabled), but there is another mandatory change in an upcomming commit to make it work there as well (see D9813) ref. T85278 Maniphest Tasks: T85278 Differential Revision: https://developer.blender.org/D10293
2021-02-03 14:39:24 +01:00
}
static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->node_group == nullptr) {
return true;
}
return false;
}
static bool logging_enabled(const ModifierEvalContext *ctx)
{
if (!DEG_is_active(ctx->depsgraph)) {
return false;
}
if ((ctx->flag & MOD_APPLY_ORCO) != 0) {
return false;
}
return true;
}
static void update_id_properties_from_node_group(NodesModifierData *nmd)
{
if (nmd->node_group == nullptr) {
if (nmd->settings.properties) {
IDP_FreeProperty(nmd->settings.properties);
nmd->settings.properties = nullptr;
}
return;
}
IDProperty *old_properties = nmd->settings.properties;
nmd->settings.properties = bke::idprop::create_group("Nodes Modifier Settings").release();
IDProperty *new_properties = nmd->settings.properties;
nodes::update_input_properties_from_node_tree(*nmd->node_group, old_properties, *new_properties);
nodes::update_output_properties_from_node_tree(
*nmd->node_group, old_properties, *new_properties);
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
if (old_properties != nullptr) {
IDP_FreeProperty(old_properties);
}
}
static void remove_outdated_bake_caches(NodesModifierData &nmd)
{
if (!nmd.runtime->cache) {
if (nmd.bakes_num == 0) {
return;
}
nmd.runtime->cache = std::make_shared<bake::ModifierCache>();
}
bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
std::lock_guard lock{modifier_cache.mutex};
Set<int> existing_bake_ids;
for (const NodesModifierBake &bake : Span{nmd.bakes, nmd.bakes_num}) {
existing_bake_ids.add(bake.id);
}
auto remove_predicate = [&](auto item) { return !existing_bake_ids.contains(item.key); };
modifier_cache.bake_cache_by_id.remove_if(remove_predicate);
modifier_cache.simulation_cache_by_id.remove_if(remove_predicate);
}
static void update_bakes_from_node_group(NodesModifierData &nmd)
{
Map<int, NodesModifierBake *> old_bake_by_id;
for (NodesModifierBake &bake : MutableSpan(nmd.bakes, nmd.bakes_num)) {
old_bake_by_id.add(bake.id, &bake);
}
Vector<int> new_bake_ids;
if (nmd.node_group) {
for (const bNestedNodeRef &ref : nmd.node_group->nested_node_refs_span()) {
const bNode *node = nmd.node_group->find_nested_node(ref.id);
if (node) {
if (ELEM(node->type_legacy, GEO_NODE_SIMULATION_OUTPUT, GEO_NODE_BAKE)) {
new_bake_ids.append(ref.id);
}
}
else if (old_bake_by_id.contains(ref.id)) {
/* Keep baked data in case linked data is missing so that it still exists when the linked
* data has been found. */
new_bake_ids.append(ref.id);
}
}
}
NodesModifierBake *new_bake_data = MEM_calloc_arrayN<NodesModifierBake>(new_bake_ids.size(),
__func__);
for (const int i : new_bake_ids.index_range()) {
const int id = new_bake_ids[i];
NodesModifierBake *old_bake = old_bake_by_id.lookup_default(id, nullptr);
NodesModifierBake &new_bake = new_bake_data[i];
if (old_bake) {
new_bake = *old_bake;
/* The ownership of this data was moved to `new_bake`. */
old_bake->directory = nullptr;
old_bake->data_blocks = nullptr;
old_bake->data_blocks_num = 0;
old_bake->packed = nullptr;
}
else {
new_bake.id = id;
new_bake.frame_start = 1;
new_bake.frame_end = 100;
new_bake.bake_mode = NODES_MODIFIER_BAKE_MODE_STILL;
}
}
for (NodesModifierBake &old_bake : MutableSpan(nmd.bakes, nmd.bakes_num)) {
nodes_modifier_bake_destruct(&old_bake, true);
}
MEM_SAFE_FREE(nmd.bakes);
nmd.bakes = new_bake_data;
nmd.bakes_num = new_bake_ids.size();
remove_outdated_bake_caches(nmd);
}
static void update_panels_from_node_group(NodesModifierData &nmd)
{
Map<int, NodesModifierPanel *> old_panel_by_id;
for (NodesModifierPanel &panel : MutableSpan(nmd.panels, nmd.panels_num)) {
old_panel_by_id.add(panel.id, &panel);
}
Vector<const bNodeTreeInterfacePanel *> interface_panels;
if (nmd.node_group) {
nmd.node_group->ensure_interface_cache();
nmd.node_group->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
if (item.item_type != NODE_INTERFACE_PANEL) {
return true;
}
interface_panels.append(reinterpret_cast<const bNodeTreeInterfacePanel *>(&item));
return true;
});
}
NodesModifierPanel *new_panels = MEM_calloc_arrayN<NodesModifierPanel>(interface_panels.size(),
__func__);
for (const int i : interface_panels.index_range()) {
const bNodeTreeInterfacePanel &interface_panel = *interface_panels[i];
const int id = interface_panel.identifier;
NodesModifierPanel *old_panel = old_panel_by_id.lookup_default(id, nullptr);
NodesModifierPanel &new_panel = new_panels[i];
if (old_panel) {
new_panel = *old_panel;
}
else {
new_panel.id = id;
const bool default_closed = interface_panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED;
SET_FLAG_FROM_TEST(new_panel.flag, !default_closed, NODES_MODIFIER_PANEL_OPEN);
}
}
MEM_SAFE_FREE(nmd.panels);
nmd.panels = new_panels;
nmd.panels_num = interface_panels.size();
}
} // namespace blender
void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
{
using namespace blender;
update_id_properties_from_node_group(nmd);
update_bakes_from_node_group(*nmd);
update_panels_from_node_group(*nmd);
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
}
NodesModifierBake *NodesModifierData::find_bake(const int id)
{
return const_cast<NodesModifierBake *>(std::as_const(*this).find_bake(id));
}
const NodesModifierBake *NodesModifierData::find_bake(const int id) const
{
for (const NodesModifierBake &bake : blender::Span{this->bakes, this->bakes_num}) {
if (bake.id == id) {
return &bake;
}
}
return nullptr;
}
namespace blender {
/**
* Setup side effects nodes so that the given node in the given compute context will be executed.
* To make sure that it is executed, all parent group nodes and zones have to be set to have side
* effects as well.
*/
static void try_add_side_effect_node(const ModifierEvalContext &ctx,
const ComputeContext &final_compute_context,
const int final_node_id,
const NodesModifierData &nmd,
nodes::GeoNodesSideEffectNodes &r_side_effect_nodes)
{
if (nmd.node_group == nullptr) {
return;
Spreadsheet: breadcrumbs and node pinning This introduces a context path to the spreadsheet editor, which contains information about what data is shown in the spreadsheet. The context path (breadcrumbs) can reference a specific node in a node group hierarchy. During object evaluation, the geometry nodes modifier checks what data is currently requested by visible spreadsheets and stores the corresponding geometry sets separately for later access. The context path can be updated by the user explicitely, by clicking on the new icon in the header of nodes. Under some circumstances, the context path is updated automatically based on Blender's context. This patch also consolidates the "Node" and "Final" object evaluation mode to just "Evaluated". Based on the current context path, either the final geometry set of an object will be displayed, or the data at a specific node. The new preview icon in geometry nodes now behaves more like a toggle. It can be clicked again to clear the context path in an open spreadsheet editor. Previously, only an object could be pinned in the spreadsheet editor. Now it is possible to pin the entire context path. That allows two different spreadsheets to display geometry data from two different nodes. The breadcrumbs in the spreadsheet header can be collapsed by clicking on the arrow icons. It's not ideal but works well for now. This might be changed again, if we get a data set region on the left. Differential Revision: https://developer.blender.org/D10931
2021-04-15 08:57:10 +02:00
}
Vector<const ComputeContext *> compute_context_vec;
for (const ComputeContext *c = &final_compute_context; c; c = c->parent()) {
compute_context_vec.append(c);
}
std::reverse(compute_context_vec.begin(), compute_context_vec.end());
const auto *modifier_compute_context = dynamic_cast<const bke::ModifierComputeContext *>(
compute_context_vec[0]);
if (modifier_compute_context == nullptr) {
return;
Spreadsheet: breadcrumbs and node pinning This introduces a context path to the spreadsheet editor, which contains information about what data is shown in the spreadsheet. The context path (breadcrumbs) can reference a specific node in a node group hierarchy. During object evaluation, the geometry nodes modifier checks what data is currently requested by visible spreadsheets and stores the corresponding geometry sets separately for later access. The context path can be updated by the user explicitely, by clicking on the new icon in the header of nodes. Under some circumstances, the context path is updated automatically based on Blender's context. This patch also consolidates the "Node" and "Final" object evaluation mode to just "Evaluated". Based on the current context path, either the final geometry set of an object will be displayed, or the data at a specific node. The new preview icon in geometry nodes now behaves more like a toggle. It can be clicked again to clear the context path in an open spreadsheet editor. Previously, only an object could be pinned in the spreadsheet editor. Now it is possible to pin the entire context path. That allows two different spreadsheets to display geometry data from two different nodes. The breadcrumbs in the spreadsheet header can be collapsed by clicking on the arrow icons. It's not ideal but works well for now. This might be changed again, if we get a data set region on the left. Differential Revision: https://developer.blender.org/D10931
2021-04-15 08:57:10 +02:00
}
if (modifier_compute_context->modifier_name() != nmd.modifier.name) {
return;
Spreadsheet: breadcrumbs and node pinning This introduces a context path to the spreadsheet editor, which contains information about what data is shown in the spreadsheet. The context path (breadcrumbs) can reference a specific node in a node group hierarchy. During object evaluation, the geometry nodes modifier checks what data is currently requested by visible spreadsheets and stores the corresponding geometry sets separately for later access. The context path can be updated by the user explicitely, by clicking on the new icon in the header of nodes. Under some circumstances, the context path is updated automatically based on Blender's context. This patch also consolidates the "Node" and "Final" object evaluation mode to just "Evaluated". Based on the current context path, either the final geometry set of an object will be displayed, or the data at a specific node. The new preview icon in geometry nodes now behaves more like a toggle. It can be clicked again to clear the context path in an open spreadsheet editor. Previously, only an object could be pinned in the spreadsheet editor. Now it is possible to pin the entire context path. That allows two different spreadsheets to display geometry data from two different nodes. The breadcrumbs in the spreadsheet header can be collapsed by clicking on the arrow icons. It's not ideal but works well for now. This might be changed again, if we get a data set region on the left. Differential Revision: https://developer.blender.org/D10931
2021-04-15 08:57:10 +02:00
}
const bNodeTree *current_tree = nmd.node_group;
const bke::bNodeTreeZone *current_zone = nullptr;
Spreadsheet: breadcrumbs and node pinning This introduces a context path to the spreadsheet editor, which contains information about what data is shown in the spreadsheet. The context path (breadcrumbs) can reference a specific node in a node group hierarchy. During object evaluation, the geometry nodes modifier checks what data is currently requested by visible spreadsheets and stores the corresponding geometry sets separately for later access. The context path can be updated by the user explicitely, by clicking on the new icon in the header of nodes. Under some circumstances, the context path is updated automatically based on Blender's context. This patch also consolidates the "Node" and "Final" object evaluation mode to just "Evaluated". Based on the current context path, either the final geometry set of an object will be displayed, or the data at a specific node. The new preview icon in geometry nodes now behaves more like a toggle. It can be clicked again to clear the context path in an open spreadsheet editor. Previously, only an object could be pinned in the spreadsheet editor. Now it is possible to pin the entire context path. That allows two different spreadsheets to display geometry data from two different nodes. The breadcrumbs in the spreadsheet header can be collapsed by clicking on the arrow icons. It's not ideal but works well for now. This might be changed again, if we get a data set region on the left. Differential Revision: https://developer.blender.org/D10931
2021-04-15 08:57:10 +02:00
Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
/* Write side effect nodes to a new map and only if everything succeeds, move the nodes to the
* caller. This is easier than changing r_side_effect_nodes directly and then undoing changes in
* case of errors. */
nodes::GeoNodesSideEffectNodes local_side_effect_nodes;
for (const ComputeContext *compute_context_generic : compute_context_vec.as_span().drop_front(1))
{
const bke::bNodeTreeZones *current_zones = current_tree->zones();
if (current_zones == nullptr) {
return;
Spreadsheet: breadcrumbs and node pinning This introduces a context path to the spreadsheet editor, which contains information about what data is shown in the spreadsheet. The context path (breadcrumbs) can reference a specific node in a node group hierarchy. During object evaluation, the geometry nodes modifier checks what data is currently requested by visible spreadsheets and stores the corresponding geometry sets separately for later access. The context path can be updated by the user explicitely, by clicking on the new icon in the header of nodes. Under some circumstances, the context path is updated automatically based on Blender's context. This patch also consolidates the "Node" and "Final" object evaluation mode to just "Evaluated". Based on the current context path, either the final geometry set of an object will be displayed, or the data at a specific node. The new preview icon in geometry nodes now behaves more like a toggle. It can be clicked again to clear the context path in an open spreadsheet editor. Previously, only an object could be pinned in the spreadsheet editor. Now it is possible to pin the entire context path. That allows two different spreadsheets to display geometry data from two different nodes. The breadcrumbs in the spreadsheet header can be collapsed by clicking on the arrow icons. It's not ideal but works well for now. This might be changed again, if we get a data set region on the left. Differential Revision: https://developer.blender.org/D10931
2021-04-15 08:57:10 +02:00
}
const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*current_tree);
Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
if (lf_graph_info == nullptr) {
return;
Spreadsheet: breadcrumbs and node pinning This introduces a context path to the spreadsheet editor, which contains information about what data is shown in the spreadsheet. The context path (breadcrumbs) can reference a specific node in a node group hierarchy. During object evaluation, the geometry nodes modifier checks what data is currently requested by visible spreadsheets and stores the corresponding geometry sets separately for later access. The context path can be updated by the user explicitely, by clicking on the new icon in the header of nodes. Under some circumstances, the context path is updated automatically based on Blender's context. This patch also consolidates the "Node" and "Final" object evaluation mode to just "Evaluated". Based on the current context path, either the final geometry set of an object will be displayed, or the data at a specific node. The new preview icon in geometry nodes now behaves more like a toggle. It can be clicked again to clear the context path in an open spreadsheet editor. Previously, only an object could be pinned in the spreadsheet editor. Now it is possible to pin the entire context path. That allows two different spreadsheets to display geometry data from two different nodes. The breadcrumbs in the spreadsheet header can be collapsed by clicking on the arrow icons. It's not ideal but works well for now. This might be changed again, if we get a data set region on the left. Differential Revision: https://developer.blender.org/D10931
2021-04-15 08:57:10 +02:00
}
const ComputeContextHash &parent_compute_context_hash =
compute_context_generic->parent()->hash();
if (const auto *compute_context = dynamic_cast<const bke::SimulationZoneComputeContext *>(
compute_context_generic))
{
const bke::bNodeTreeZone *simulation_zone = current_zones->get_zone_by_node(
compute_context->output_node_id());
if (simulation_zone == nullptr) {
return;
Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
}
if (simulation_zone->parent_zone != current_zone) {
return;
Geometry Nodes: new Repeat Zone This adds support for running a set of nodes repeatedly. The number of iterations can be controlled dynamically as an input of the repeat zone. The repeat zone can be added in via the search or from the Add > Utilities menu. The main use case is to replace long repetitive node chains with a more flexible alternative. Technically, repeat zones can also be used for many other use cases. However, due to their serial nature, performance is very sub-optimal when they are used to solve problems that could be processed in parallel. Better solutions for such use cases will be worked on separately. Repeat zones are similar to simulation zones. The major difference is that they have no concept of time and are always evaluated entirely in the current frame, while in simulations only a single iteration is evaluated per frame. Stopping the repetition early using a dynamic condition is not yet supported. "Break" functionality can be implemented manually using Switch nodes in the loop for now. It's likely that this functionality will be built into the repeat zone in the future. For now, things are kept more simple. Remaining Todos after this first version: * Improve socket inspection and viewer node support. Currently, only the first iteration is taken into account for socket inspection and the viewer. * Make loop evaluation more lazy. Currently, the evaluation is eager, meaning that it evaluates some nodes even though their output may not be required. Pull Request: https://projects.blender.org/blender/blender/pulls/109164
2023-07-11 22:36:10 +02:00
}
const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default(
simulation_zone, nullptr);
if (lf_zone_node == nullptr) {
return;
Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
}
const lf::FunctionNode *lf_simulation_output_node =
lf_graph_info->mapping.possible_side_effect_node_map.lookup_default(
simulation_zone->output_node, nullptr);
if (lf_simulation_output_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_zone_node);
/* By making the simulation output node a side-effect-node, we can ensure that the simulation
* runs when it contains an active viewer. */
local_side_effect_nodes.nodes_by_context.add(compute_context_generic->hash(),
lf_simulation_output_node);
current_zone = simulation_zone;
}
else if (const auto *compute_context = dynamic_cast<const bke::RepeatZoneComputeContext *>(
compute_context_generic))
{
const bke::bNodeTreeZone *repeat_zone = current_zones->get_zone_by_node(
compute_context->output_node_id());
if (repeat_zone == nullptr) {
return;
}
if (repeat_zone->parent_zone != current_zone) {
return;
}
const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default(
repeat_zone, nullptr);
if (lf_zone_node == nullptr) {
Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
return;
}
local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_zone_node);
Geometry Nodes: new For Each Geometry Element zone This adds a new type of zone to Geometry Nodes that allows executing some nodes for each element in a geometry. ## Features * The `Selection` input allows iterating over a subset of elements on the set domain. * Fields passed into the input node are available as single values inside of the zone. * The input geometry can be split up into separate (completely independent) geometries for each element (on all domains except face corner). * New attributes can be created on the input geometry by outputting a single value from each iteration. * New geometries can be generated in each iteration. * All of these geometries are joined to form the final output. * Attributes from the input geometry are propagated to the output geometries. ## Evaluation The evaluation strategy is similar to the one used for repeat zones. Namely, it dynamically builds a `lazy_function::Graph` once it knows how many iterations are necessary. It contains a separate node for each iteration. The inputs for each iteration are hardcoded into the graph. The outputs of each iteration a passed to a separate lazy-function that reduces all the values down to the final outputs. This final output can have a huge number of inputs and that is not ideal for multi-threading yet, but that can still be improved in the future. ## Performance There is a non-neglilible amount of overhead for each iteration. The overhead is way larger than the per-element overhead when just doing field evaluation. Therefore, normal field evaluation should be preferred when possible. That can partially still be optimized if there is only some number crunching going on in the zone but that optimization is not implemented yet. However, processing many small geometries (e.g. each hair of a character separately) will likely **always be slower** than working on fewer larger geoemtries. The additional flexibility you get by processing each element separately comes at the cost that Blender can't optimize the operation as well. For node groups that need to handle lots of geometry elements, we recommend trying to design the node setup so that iteration over tiny sub-geometries is not required. An opposite point is true as well though. It can be faster to process more medium sized geometries in parallel than fewer very large geometries because of more multi-threading opportunities. The exact threshold between tiny, medium and large geometries depends on a lot of factors though. Overall, this initial version of the new zone does not implement all optimization opportunities yet, but the points mentioned above will still hold true later. Pull Request: https://projects.blender.org/blender/blender/pulls/127331
2024-09-24 11:52:02 +02:00
local_side_effect_nodes.iterations_by_iteration_zone.add(
{parent_compute_context_hash, compute_context->output_node_id()},
compute_context->iteration());
current_zone = repeat_zone;
}
Geometry Nodes: new For Each Geometry Element zone This adds a new type of zone to Geometry Nodes that allows executing some nodes for each element in a geometry. ## Features * The `Selection` input allows iterating over a subset of elements on the set domain. * Fields passed into the input node are available as single values inside of the zone. * The input geometry can be split up into separate (completely independent) geometries for each element (on all domains except face corner). * New attributes can be created on the input geometry by outputting a single value from each iteration. * New geometries can be generated in each iteration. * All of these geometries are joined to form the final output. * Attributes from the input geometry are propagated to the output geometries. ## Evaluation The evaluation strategy is similar to the one used for repeat zones. Namely, it dynamically builds a `lazy_function::Graph` once it knows how many iterations are necessary. It contains a separate node for each iteration. The inputs for each iteration are hardcoded into the graph. The outputs of each iteration a passed to a separate lazy-function that reduces all the values down to the final outputs. This final output can have a huge number of inputs and that is not ideal for multi-threading yet, but that can still be improved in the future. ## Performance There is a non-neglilible amount of overhead for each iteration. The overhead is way larger than the per-element overhead when just doing field evaluation. Therefore, normal field evaluation should be preferred when possible. That can partially still be optimized if there is only some number crunching going on in the zone but that optimization is not implemented yet. However, processing many small geometries (e.g. each hair of a character separately) will likely **always be slower** than working on fewer larger geoemtries. The additional flexibility you get by processing each element separately comes at the cost that Blender can't optimize the operation as well. For node groups that need to handle lots of geometry elements, we recommend trying to design the node setup so that iteration over tiny sub-geometries is not required. An opposite point is true as well though. It can be faster to process more medium sized geometries in parallel than fewer very large geometries because of more multi-threading opportunities. The exact threshold between tiny, medium and large geometries depends on a lot of factors though. Overall, this initial version of the new zone does not implement all optimization opportunities yet, but the points mentioned above will still hold true later. Pull Request: https://projects.blender.org/blender/blender/pulls/127331
2024-09-24 11:52:02 +02:00
else if (const auto *compute_context =
dynamic_cast<const bke::ForeachGeometryElementZoneComputeContext *>(
compute_context_generic))
{
const bke::bNodeTreeZone *foreach_zone = current_zones->get_zone_by_node(
compute_context->output_node_id());
if (foreach_zone == nullptr) {
return;
}
if (foreach_zone->parent_zone != current_zone) {
return;
}
const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default(
foreach_zone, nullptr);
if (lf_zone_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_zone_node);
local_side_effect_nodes.iterations_by_iteration_zone.add(
{parent_compute_context_hash, compute_context->output_node_id()},
compute_context->index());
current_zone = foreach_zone;
}
else if (const auto *compute_context = dynamic_cast<const bke::GroupNodeComputeContext *>(
compute_context_generic))
{
const bNode *group_node = current_tree->node_by_id(compute_context->node_id());
if (group_node == nullptr) {
return;
}
if (group_node->id == nullptr) {
return;
}
if (group_node->is_muted()) {
return;
}
if (current_zone != current_zones->get_zone_by_node(group_node->identifier)) {
return;
}
const lf::FunctionNode *lf_group_node = lf_graph_info->mapping.group_node_map.lookup_default(
group_node, nullptr);
if (lf_group_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_group_node);
current_tree = reinterpret_cast<const bNodeTree *>(group_node->id);
current_zone = nullptr;
}
else if (const auto *compute_context =
dynamic_cast<const bke::EvaluateClosureComputeContext *>(compute_context_generic))
{
const bNode *evaluate_node = current_tree->node_by_id(compute_context->node_id());
if (!evaluate_node) {
return;
}
if (evaluate_node->is_muted()) {
return;
}
if (current_zone != current_zones->get_zone_by_node(evaluate_node->identifier)) {
return;
}
const std::optional<nodes::ClosureSourceLocation> &source_location =
compute_context->closure_source_location();
if (!source_location) {
return;
}
if (!source_location->tree->zones()) {
return;
}
const lf::FunctionNode *lf_evaluate_node =
lf_graph_info->mapping.possible_side_effect_node_map.lookup_default(evaluate_node,
nullptr);
if (!lf_evaluate_node) {
return;
}
/* The tree may sometimes be original and sometimes evaluated, depending on the source of the
* compute context. */
const bNodeTree *eval_closure_tree = DEG_is_evaluated(source_location->tree) ?
source_location->tree :
reinterpret_cast<const bNodeTree *>(
DEG_get_evaluated_id(
ctx.depsgraph, &source_location->tree->id));
const bNode *closure_output_node = eval_closure_tree->node_by_id(
source_location->closure_output_node_id);
if (!closure_output_node) {
return;
}
local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_evaluate_node);
current_tree = eval_closure_tree;
current_zone = eval_closure_tree->zones()->get_zone_by_node(closure_output_node->identifier);
}
else {
return;
}
Spreadsheet: breadcrumbs and node pinning This introduces a context path to the spreadsheet editor, which contains information about what data is shown in the spreadsheet. The context path (breadcrumbs) can reference a specific node in a node group hierarchy. During object evaluation, the geometry nodes modifier checks what data is currently requested by visible spreadsheets and stores the corresponding geometry sets separately for later access. The context path can be updated by the user explicitely, by clicking on the new icon in the header of nodes. Under some circumstances, the context path is updated automatically based on Blender's context. This patch also consolidates the "Node" and "Final" object evaluation mode to just "Evaluated". Based on the current context path, either the final geometry set of an object will be displayed, or the data at a specific node. The new preview icon in geometry nodes now behaves more like a toggle. It can be clicked again to clear the context path in an open spreadsheet editor. Previously, only an object could be pinned in the spreadsheet editor. Now it is possible to pin the entire context path. That allows two different spreadsheets to display geometry data from two different nodes. The breadcrumbs in the spreadsheet header can be collapsed by clicking on the arrow icons. It's not ideal but works well for now. This might be changed again, if we get a data set region on the left. Differential Revision: https://developer.blender.org/D10931
2021-04-15 08:57:10 +02:00
}
const bNode *final_node = current_tree->node_by_id(final_node_id);
if (final_node == nullptr) {
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
return;
}
const auto *lf_graph_info = nodes::ensure_geometry_nodes_lazy_function_graph(*current_tree);
Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
if (lf_graph_info == nullptr) {
return;
}
const bke::bNodeTreeZones *tree_zones = current_tree->zones();
Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
if (tree_zones == nullptr) {
return;
}
if (tree_zones->get_zone_by_node(final_node_id) != current_zone) {
Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
return;
}
const lf::FunctionNode *lf_node =
lf_graph_info->mapping.possible_side_effect_node_map.lookup_default(final_node, nullptr);
if (lf_node == nullptr) {
return;
}
local_side_effect_nodes.nodes_by_context.add(final_compute_context.hash(), lf_node);
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
/* Successfully found all side effect nodes for the viewer path. */
for (const auto item : local_side_effect_nodes.nodes_by_context.items()) {
r_side_effect_nodes.nodes_by_context.add_multiple(item.key, item.value);
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
}
Geometry Nodes: new For Each Geometry Element zone This adds a new type of zone to Geometry Nodes that allows executing some nodes for each element in a geometry. ## Features * The `Selection` input allows iterating over a subset of elements on the set domain. * Fields passed into the input node are available as single values inside of the zone. * The input geometry can be split up into separate (completely independent) geometries for each element (on all domains except face corner). * New attributes can be created on the input geometry by outputting a single value from each iteration. * New geometries can be generated in each iteration. * All of these geometries are joined to form the final output. * Attributes from the input geometry are propagated to the output geometries. ## Evaluation The evaluation strategy is similar to the one used for repeat zones. Namely, it dynamically builds a `lazy_function::Graph` once it knows how many iterations are necessary. It contains a separate node for each iteration. The inputs for each iteration are hardcoded into the graph. The outputs of each iteration a passed to a separate lazy-function that reduces all the values down to the final outputs. This final output can have a huge number of inputs and that is not ideal for multi-threading yet, but that can still be improved in the future. ## Performance There is a non-neglilible amount of overhead for each iteration. The overhead is way larger than the per-element overhead when just doing field evaluation. Therefore, normal field evaluation should be preferred when possible. That can partially still be optimized if there is only some number crunching going on in the zone but that optimization is not implemented yet. However, processing many small geometries (e.g. each hair of a character separately) will likely **always be slower** than working on fewer larger geoemtries. The additional flexibility you get by processing each element separately comes at the cost that Blender can't optimize the operation as well. For node groups that need to handle lots of geometry elements, we recommend trying to design the node setup so that iteration over tiny sub-geometries is not required. An opposite point is true as well though. It can be faster to process more medium sized geometries in parallel than fewer very large geometries because of more multi-threading opportunities. The exact threshold between tiny, medium and large geometries depends on a lot of factors though. Overall, this initial version of the new zone does not implement all optimization opportunities yet, but the points mentioned above will still hold true later. Pull Request: https://projects.blender.org/blender/blender/pulls/127331
2024-09-24 11:52:02 +02:00
for (const auto item : local_side_effect_nodes.iterations_by_iteration_zone.items()) {
r_side_effect_nodes.iterations_by_iteration_zone.add_multiple(item.key, item.value);
Geometry Nodes: use lazy evaluation in repeat zone The goal is to make the evaluation of repeat zones more efficient by making use of the lazy-function evaluation system. Performance is improved in two ways: * Unnecessary nodes are not evaluated anymore. E.g. if a repeat zone outputs two geometries but only one of those is actually used, the other one will not be computed anymore. * Support evaluating different iteration indices at the same time on different threads. It is possible that some uses of repeat zones become slower with this refactor, especially when each iteration does very little work and there are a lot of iterations. The old implementation was not optimized for this use case either but now there is a bit more overhead constant overhead per iteration than before. On the bright side, this change can result in some very significant speedups when some computations can be skipped. See #112421 for some example setups. There is one todo comment for adding back-links for socket usages. Properly linking those up can result in better (shorter) life-times for anonymous attributes. Even without that, performance is already better than before. The implementation reuses the existing lazy-function graph system for the repeat zone, by building a dynamically sized graph based on the number of iterations. Building a lazy-function graph makes it possible to use the lazyness and multi-threading features of the lazy-function graph executor. This is much easier than reimplementing this behavior for repeat zones specifically (hence there was only single-threaded eager execution before). Pull Request: https://projects.blender.org/blender/blender/pulls/112421
2023-09-22 08:58:16 +02:00
}
}
static void find_side_effect_nodes_for_viewer_path(
const ViewerPath &viewer_path,
const NodesModifierData &nmd,
const ModifierEvalContext &ctx,
nodes::GeoNodesSideEffectNodes &r_side_effect_nodes)
{
const std::optional<ed::viewer_path::ViewerPathForGeometryNodesViewer> parsed_path =
ed::viewer_path::parse_geometry_nodes_viewer(viewer_path);
if (!parsed_path.has_value()) {
return;
}
if (parsed_path->object != DEG_get_original(ctx.object)) {
return;
}
if (parsed_path->modifier_name != nmd.modifier.name) {
return;
}
bke::ComputeContextCache compute_context_cache;
const ComputeContext *current = &compute_context_cache.for_modifier(nullptr, nmd);
for (const ViewerPathElem *elem : parsed_path->node_path) {
current = ed::viewer_path::compute_context_for_viewer_path_elem(
*elem, compute_context_cache, current);
if (!current) {
return;
}
}
try_add_side_effect_node(ctx, *current, parsed_path->viewer_node_id, nmd, r_side_effect_nodes);
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
static void find_side_effect_nodes_for_nested_node(
const ModifierEvalContext &ctx,
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
const NodesModifierData &nmd,
const int root_nested_node_id,
nodes::GeoNodesSideEffectNodes &r_side_effect_nodes)
{
bke::ComputeContextCache compute_context_cache;
const ComputeContext *compute_context = &compute_context_cache.for_modifier(nullptr, nmd);
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
int nested_node_id = root_nested_node_id;
const bNodeTree *tree = nmd.node_group;
while (true) {
const bNestedNodeRef *ref = tree->find_nested_node_ref(nested_node_id);
if (!ref) {
return;
}
const bNode *node = tree->node_by_id(ref->path.node_id);
if (!node) {
return;
}
const bke::bNodeTreeZones *zones = tree->zones();
if (!zones) {
return;
}
if (zones->get_zone_by_node(node->identifier) != nullptr) {
/* Only top level nodes are allowed here. */
return;
}
if (node->is_group()) {
if (!node->id) {
return;
}
compute_context = &compute_context_cache.for_group_node(compute_context, *node, *tree);
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
tree = reinterpret_cast<const bNodeTree *>(node->id);
nested_node_id = ref->path.id_in_node;
}
else {
try_add_side_effect_node(ctx, *compute_context, ref->path.node_id, nmd, r_side_effect_nodes);
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
return;
}
}
}
/**
* This ensures that nodes that the user wants to bake are actually evaluated. Otherwise they might
* not be if they are not connected to the output.
*/
static void find_side_effect_nodes_for_baking(const NodesModifierData &nmd,
const ModifierEvalContext &ctx,
nodes::GeoNodesSideEffectNodes &r_side_effect_nodes)
{
if (!nmd.runtime->cache) {
return;
}
if (!DEG_is_active(ctx.depsgraph)) {
/* Only the active depsgraph can bake. */
return;
}
bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
for (const bNestedNodeRef &ref : nmd.node_group->nested_node_refs_span()) {
if (!modifier_cache.requested_bakes.contains(ref.id)) {
continue;
}
find_side_effect_nodes_for_nested_node(ctx, nmd, ref.id, r_side_effect_nodes);
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
}
}
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
static void find_side_effect_nodes_for_active_gizmos(
const NodesModifierData &nmd,
const ModifierEvalContext &ctx,
const wmWindowManager &wm,
nodes::GeoNodesSideEffectNodes &r_side_effect_nodes,
Set<ComputeContextHash> &r_socket_log_contexts)
{
Object *object_orig = DEG_get_original(ctx.object);
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
const NodesModifierData &nmd_orig = *reinterpret_cast<const NodesModifierData *>(
BKE_modifier_get_original(ctx.object, const_cast<ModifierData *>(&nmd.modifier)));
bke::ComputeContextCache compute_context_cache;
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
nodes::gizmos::foreach_active_gizmo_in_modifier(
*object_orig,
nmd_orig,
wm,
compute_context_cache,
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
[&](const ComputeContext &compute_context,
const bNode &gizmo_node,
const bNodeSocket &gizmo_socket) {
try_add_side_effect_node(
ctx, compute_context, gizmo_node.identifier, nmd, r_side_effect_nodes);
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
r_socket_log_contexts.add(compute_context.hash());
nodes::gizmos::foreach_compute_context_on_gizmo_path(
compute_context, gizmo_node, gizmo_socket, [&](const ComputeContext &node_context) {
/* Make sure that all intermediate sockets are logged. This is necessary to be able
* to evaluate the nodes in reverse for the gizmo. */
r_socket_log_contexts.add(node_context.hash());
});
});
}
static void find_side_effect_nodes(const NodesModifierData &nmd,
const ModifierEvalContext &ctx,
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
nodes::GeoNodesSideEffectNodes &r_side_effect_nodes,
Set<ComputeContextHash> &r_socket_log_contexts)
{
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
Main *bmain = DEG_get_bmain(ctx.depsgraph);
Geometry Nodes: viewport preview This adds support for showing geometry passed to the Viewer in the 3d viewport (instead of just in the spreadsheet). The "viewer geometry" bypasses the group output. So it is not necessary to change the final output of the node group to be able to see the intermediate geometry. **Activation and deactivation of a viewer node** * A viewer node is activated by clicking on it. * Ctrl+shift+click on any node/socket connects it to the viewer and makes it active. * Ctrl+shift+click in empty space deactivates the active viewer. * When the active viewer is not visible anymore (e.g. another object is selected, or the current node group is exit), it is deactivated. * Clicking on the icon in the header of the Viewer node toggles whether its active or not. **Pinning** * The spreadsheet still allows pinning the active viewer as before. When pinned, the spreadsheet still references the viewer node even when it becomes inactive. * The viewport does not support pinning at the moment. It always shows the active viewer. **Attribute** * When a field is linked to the second input of the viewer node it is displayed as an overlay in the viewport. * When possible the correct domain for the attribute is determined automatically. This does not work in all cases. It falls back to the face corner domain on meshes and the point domain on curves. When necessary, the domain can be picked manually. * The spreadsheet now only shows the "Viewer" column for the domain that is selected in the Viewer node. * Instance attributes are visualized as a constant color per instance. **Viewport Options** * The attribute overlay opacity can be controlled with the "Viewer Node" setting in the overlays popover. * A viewport can be configured not to show intermediate viewer-geometry by disabling the "Viewer Node" option in the "View" menu. **Implementation Details** * The "spreadsheet context path" was generalized to a "viewer path" that is used in more places now. * The viewer node itself determines the attribute domain, evaluates the field and stores the result in a `.viewer` attribute. * A new "viewer attribute' overlay displays the data from the `.viewer` attribute. * The ground truth for the active viewer node is stored in the workspace now. Node editors, spreadsheets and viewports retrieve the active viewer from there unless they are pinned. * The depsgraph object iterator has a new "viewer path" setting. When set, the viewed geometry of the corresponding object is part of the iterator instead of the final evaluated geometry. * To support the instance attribute overlay `DupliObject` was extended to contain the information necessary for drawing the overlay. * The ctrl+shift+click operator has been refactored so that it can make existing links to viewers active again. * The auto-domain-detection in the Viewer node works by checking the "preferred domain" for every field input. If there is not exactly one preferred domain, the fallback is used. Known limitations: * Loose edges of meshes don't have the attribute overlay. This could be added separately if necessary. * Some attributes are hard to visualize as a color directly. For example, the values might have to be normalized or some should be drawn as arrays. For now, we encourage users to build node groups that generate appropriate viewer-geometry. We might include some of that functionality in future versions. Support for displaying attribute values as text in the viewport is planned as well. * There seems to be an issue with the attribute overlay for pointclouds on nvidia gpus, to be investigated. Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
if (wm == nullptr) {
return;
}
LISTBASE_FOREACH (const wmWindow *, window, &wm->windows) {
const bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
const WorkSpace *workspace = BKE_workspace_active_get(window->workspace_hook);
find_side_effect_nodes_for_viewer_path(workspace->viewer_path, nmd, ctx, r_side_effect_nodes);
LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) {
const SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
if (sl->spacetype == SPACE_SPREADSHEET) {
const SpaceSpreadsheet &sspreadsheet = *reinterpret_cast<const SpaceSpreadsheet *>(sl);
find_side_effect_nodes_for_viewer_path(
sspreadsheet.viewer_path, nmd, ctx, r_side_effect_nodes);
}
if (sl->spacetype == SPACE_VIEW3D) {
const View3D &v3d = *reinterpret_cast<const View3D *>(sl);
find_side_effect_nodes_for_viewer_path(v3d.viewer_path, nmd, ctx, r_side_effect_nodes);
}
}
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
find_side_effect_nodes_for_baking(nmd, ctx, r_side_effect_nodes);
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
find_side_effect_nodes_for_active_gizmos(
nmd, ctx, *wm, r_side_effect_nodes, r_socket_log_contexts);
}
static void find_socket_log_contexts(const NodesModifierData &nmd,
const ModifierEvalContext &ctx,
Set<ComputeContextHash> &r_socket_log_contexts)
{
Main *bmain = DEG_get_bmain(ctx.depsgraph);
wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
if (wm == nullptr) {
return;
}
bke::ComputeContextCache compute_context_cache;
LISTBASE_FOREACH (const wmWindow *, window, &wm->windows) {
const bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) {
const SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
if (sl->spacetype == SPACE_NODE) {
const SpaceNode &snode = *reinterpret_cast<const SpaceNode *>(sl);
if (snode.edittree == nullptr || snode.edittree->type != NTREE_GEOMETRY) {
continue;
}
if (!ed::space_node::node_editor_is_for_geometry_nodes_modifier(snode, *ctx.object, nmd)) {
continue;
}
const Map<const bke::bNodeTreeZone *, ComputeContextHash> hash_by_zone =
geo_log::GeoModifierLog::get_context_hash_by_zone_for_node_editor(
snode, compute_context_cache);
Geometry Nodes: make evaluation and logging system aware of zones This refactors how a geometry nodes node tree is converted to a lazy-function graph. Previously, all nodes were inserted into a single graph. This was fine because every node was evaluated at most once per node group evaluation. However, loops (#108896) break this assumption since now nodes may be evaluated multiple times and thus a single flat graph does not work anymore. Now, a separate lazy-function is build for every zone which gives us much more flexibility for what can happen in a zone. Right now, the change only applies to simulation zones since that's the only kind of zone we have. Technically, those zones could be inlined, but turning them into a separate lazy-function also does not hurt and makes it possible to test this refactor without implementing loops first. Also, having them as separate functions might help in the future if we integrate a substep loop directly into the simulation zone. The most tricky part here is to just link everything up correctly, especially with respect to deterministic anonymous attribute lifetimes. Fortunately, correctness can be checked visually by looking at the generated graphs. The logging/viewer system also had to be refactored a bit, because now there can be multiple different `ComputeContext` in a single node tree. Each zone is in a separate `ComputeContext`. To make it work, the `ViewerPath` system now explicitly supports zones and drawing code will look up the right logger for showing inspection data. No functional changes are expected, except that the spreadsheet now shows "Simulation Zone" in the context path if the viewer is in a simulation.
2023-06-20 09:50:44 +02:00
for (const ComputeContextHash &hash : hash_by_zone.values()) {
r_socket_log_contexts.add(hash);
}
}
}
}
}
/**
* \note This could be done in #initialize_group_input, though that would require adding the
* the object as a parameter, so it's likely better to this check as a separate step.
*/
static void check_property_socket_sync(const Object *ob,
const nodes::PropertiesVectorSet &properties,
ModifierData *md)
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
int geometry_socket_count = 0;
nmd->node_group->ensure_interface_cache();
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
for (const int i : nmd->node_group->interface_inputs().index_range()) {
const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_inputs()[i];
const bke::bNodeSocketType *typeinfo = socket->socket_typeinfo();
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
Geometry Nodes: add Closures and Bundles behind experimental feature flag This implements bundles and closures which are described in more detail in this blog post: https://code.blender.org/2024/11/geometry-nodes-workshop-october-2024/ tl;dr: * Bundles are containers that allow storing multiple socket values in a single value. Each value in the bundle is identified by a name. Bundles can be nested. * Closures are functions that are created with the Closure Zone and can be evaluated with the Evaluate Closure node. To use the patch, the `Bundle and Closure Nodes` experimental feature has to be enabled. This is necessary, because these features are not fully done yet and still need iterations to improve the workflow before they can be officially released. These iterations are easier to do in `main` than in a separate branch though. That's because this patch is quite large and somewhat prone to merge conflicts. Also other work we want to do, depends on this. This adds the following new nodes: * Combine Bundle: can pack multiple values into one. * Separate Bundle: extracts values from a bundle. * Closure Zone: outputs a closure zone for use in the `Evaluate Closure` node. * Evaluate Closure: evaluates the passed in closure. Things that will be added soon after this lands: * Fields in bundles and closures. The way this is done changes with #134811, so I rather implement this once both are in `main`. * UI features for keeping sockets in sync (right now there are warnings only). One bigger issue is the limited support for lazyness. For example, all inputs of a Combine Bundle node will be evaluated, even if they are not all needed. The same is true for all captured values of a closure. This is a deeper limitation that needs to be resolved at some point. This will likely be done after an initial version of this patch is done. Pull Request: https://projects.blender.org/blender/blender/pulls/128340
2025-04-03 15:44:06 +02:00
if (type == SOCK_GEOMETRY) {
geometry_socket_count++;
}
/* The first socket is the special geometry socket for the modifier object. */
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
if (i == 0 && type == SOCK_GEOMETRY) {
continue;
}
IDProperty *property = properties.lookup_key_default_as(socket->identifier, nullptr);
if (property == nullptr) {
Geometry Nodes: add Closures and Bundles behind experimental feature flag This implements bundles and closures which are described in more detail in this blog post: https://code.blender.org/2024/11/geometry-nodes-workshop-october-2024/ tl;dr: * Bundles are containers that allow storing multiple socket values in a single value. Each value in the bundle is identified by a name. Bundles can be nested. * Closures are functions that are created with the Closure Zone and can be evaluated with the Evaluate Closure node. To use the patch, the `Bundle and Closure Nodes` experimental feature has to be enabled. This is necessary, because these features are not fully done yet and still need iterations to improve the workflow before they can be officially released. These iterations are easier to do in `main` than in a separate branch though. That's because this patch is quite large and somewhat prone to merge conflicts. Also other work we want to do, depends on this. This adds the following new nodes: * Combine Bundle: can pack multiple values into one. * Separate Bundle: extracts values from a bundle. * Closure Zone: outputs a closure zone for use in the `Evaluate Closure` node. * Evaluate Closure: evaluates the passed in closure. Things that will be added soon after this lands: * Fields in bundles and closures. The way this is done changes with #134811, so I rather implement this once both are in `main`. * UI features for keeping sockets in sync (right now there are warnings only). One bigger issue is the limited support for lazyness. For example, all inputs of a Combine Bundle node will be evaluated, even if they are not all needed. The same is true for all captured values of a closure. This is a deeper limitation that needs to be resolved at some point. This will likely be done after an initial version of this patch is done. Pull Request: https://projects.blender.org/blender/blender/pulls/128340
2025-04-03 15:44:06 +02:00
if (!ELEM(type, SOCK_GEOMETRY, SOCK_MATRIX, SOCK_BUNDLE, SOCK_CLOSURE)) {
BKE_modifier_set_error(
ob, md, "Missing property for input socket \"%s\"", socket->name ? socket->name : "");
}
continue;
}
if (!nodes::id_property_type_matches_socket(*socket, *property)) {
BKE_modifier_set_error(ob,
md,
"Property type does not match input socket \"(%s)\"",
socket->name ? socket->name : "");
continue;
}
}
if (geometry_socket_count == 1) {
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const bNodeTreeInterfaceSocket *first_socket = nmd->node_group->interface_inputs()[0];
const bke::bNodeSocketType *typeinfo = first_socket->socket_typeinfo();
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
if (type != SOCK_GEOMETRY) {
BKE_modifier_set_error(ob, md, "Node group's geometry input must be the first");
}
}
}
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
class NodesModifierBakeDataBlockMap : public bake::BakeDataBlockMap {
/** Protects access to `new_mappings` which may be added to from multiple threads. */
BLI: use blender::Mutex by default which wraps tbb::mutex This patch adds a new `BLI_mutex.hh` header which adds `blender::Mutex` as alias for either `tbb::mutex` or `std::mutex` depending on whether TBB is enabled. Description copied from the patch: ``` /** * blender::Mutex should be used as the default mutex in Blender. It implements a subset of the API * of std::mutex but has overall better guaranteed properties. It can be used with RAII helpers * like std::lock_guard. However, it is not compatible with e.g. std::condition_variable. So one * still has to use std::mutex for that case. * * The mutex provided by TBB has these properties: * - It's as fast as a spin-lock in the non-contended case, i.e. when no other thread is trying to * lock the mutex at the same time. * - In the contended case, it spins a couple of times but then blocks to avoid draining system * resources by spinning for a long time. * - It's only 1 byte large, compared to e.g. 40 bytes when using the std::mutex of GCC. This makes * it more feasible to have many smaller mutexes which can improve scalability of algorithms * compared to using fewer larger mutexes. Also it just reduces "memory slop" across Blender. * - It is *not* a fair mutex, i.e. it's not guaranteed that a thread will ever be able to lock the * mutex when there are always more than one threads that try to lock it. In the majority of * cases, using a fair mutex just causes extra overhead without any benefit. std::mutex is not * guaranteed to be fair either. */ ``` The performance benchmark suggests that the impact is negilible in almost all cases. The only benchmarks that show interesting behavior are the once testing foreach zones in Geometry Nodes. These tests are explicitly testing overhead, which I still have to reduce over time. So it's not unexpected that changing the mutex has an impact there. What's interesting is that on macos the performance improves a lot while on linux it gets worse. Since that overhead should eventually be removed almost entirely, I don't really consider that blocking. Links: * Documentation of different mutex flavors in TBB: https://www.intel.com/content/www/us/en/docs/onetbb/developer-guide-api-reference/2021-12/mutex-flavors.html * Older implementation of a similar mutex by me: https://archive.blender.org/developer/differential/0016/0016711/index.html * Interesting read regarding how a mutex can be this small: https://webkit.org/blog/6161/locking-in-webkit/ Pull Request: https://projects.blender.org/blender/blender/pulls/138370
2025-05-07 04:53:16 +02:00
Mutex mutex_;
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
public:
Map<bake::BakeDataBlockID, ID *> old_mappings;
Map<bake::BakeDataBlockID, ID *> new_mappings;
ID *lookup_or_remember_missing(const bake::BakeDataBlockID &key) override
{
if (ID *id = this->old_mappings.lookup_default(key, nullptr)) {
return id;
}
if (this->old_mappings.contains(key)) {
/* Don't allow overwriting old mappings. */
return nullptr;
}
std::lock_guard lock{mutex_};
return this->new_mappings.lookup_or_add(key, nullptr);
}
void try_add(ID &id) override
{
bake::BakeDataBlockID key{id};
if (this->old_mappings.contains(key)) {
return;
}
std::lock_guard lock{mutex_};
this->new_mappings.add_overwrite(std::move(key), &id);
}
private:
ID *lookup_in_map(Map<bake::BakeDataBlockID, ID *> &map,
const bake::BakeDataBlockID &key,
const std::optional<ID_Type> &type)
{
ID *id = map.lookup_default(key, nullptr);
if (!id) {
return nullptr;
}
if (type && GS(id->name) != *type) {
return nullptr;
}
return id;
}
};
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
namespace sim_input = nodes::sim_input;
namespace sim_output = nodes::sim_output;
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
struct BakeFrameIndices {
std::optional<int> prev;
std::optional<int> current;
std::optional<int> next;
};
static BakeFrameIndices get_bake_frame_indices(
const Span<std::unique_ptr<bake::FrameCache>> frame_caches, const SubFrame frame)
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
{
BakeFrameIndices frame_indices;
if (!frame_caches.is_empty()) {
const int first_future_frame_index = binary_search::first_if(
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
frame_caches,
[&](const std::unique_ptr<bake::FrameCache> &value) { return value->frame > frame; });
frame_indices.next = (first_future_frame_index == frame_caches.size()) ?
std::nullopt :
std::optional<int>(first_future_frame_index);
if (first_future_frame_index > 0) {
const int index = first_future_frame_index - 1;
if (frame_caches[index]->frame < frame) {
frame_indices.prev = index;
}
else {
BLI_assert(frame_caches[index]->frame == frame);
frame_indices.current = index;
if (index > 0) {
frame_indices.prev = index - 1;
}
}
}
}
return frame_indices;
}
static void ensure_bake_loaded(bake::NodeBakeCache &bake_cache, bake::FrameCache &frame_cache)
{
if (!frame_cache.state.items_by_id.is_empty()) {
return;
}
if (!frame_cache.meta_data_source.has_value()) {
return;
}
if (bake_cache.memory_blob_reader) {
if (const auto *meta_buffer = std::get_if<Span<std::byte>>(&*frame_cache.meta_data_source)) {
const std::string meta_str{reinterpret_cast<const char *>(meta_buffer->data()),
size_t(meta_buffer->size())};
std::istringstream meta_stream{meta_str};
std::optional<bake::BakeState> bake_state = bake::deserialize_bake(
meta_stream, *bake_cache.memory_blob_reader, *bake_cache.blob_sharing);
if (!bake_state.has_value()) {
return;
}
frame_cache.state = std::move(*bake_state);
return;
}
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
if (!bake_cache.blobs_dir) {
return;
}
const auto *meta_path = std::get_if<std::string>(&*frame_cache.meta_data_source);
if (!meta_path) {
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
return;
}
bake::DiskBlobReader blob_reader{*bake_cache.blobs_dir};
fstream meta_file{*meta_path};
std::optional<bake::BakeState> bake_state = bake::deserialize_bake(
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
meta_file, blob_reader, *bake_cache.blob_sharing);
if (!bake_state.has_value()) {
return;
}
frame_cache.state = std::move(*bake_state);
}
static bool try_find_baked_data(const NodesModifierBake &bake,
bake::NodeBakeCache &bake_cache,
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
const Main &bmain,
const Object &object,
const NodesModifierData &nmd,
const int id)
{
if (bake.packed) {
if (bake.packed->meta_files_num == 0) {
return false;
}
bake_cache.reset();
Map<SubFrame, const NodesModifierBakeFile *> file_by_frame;
for (const NodesModifierBakeFile &meta_file :
Span{bake.packed->meta_files, bake.packed->meta_files_num})
{
const std::optional<SubFrame> frame = bake::file_name_to_frame(meta_file.name);
if (!frame) {
return false;
}
if (!file_by_frame.add(*frame, &meta_file)) {
/* Can only have on file per (sub)frame. */
return false;
}
}
/* Make sure frames processed in the right order. */
Vector<SubFrame> frames;
frames.extend(file_by_frame.keys().begin(), file_by_frame.keys().end());
for (const SubFrame &frame : frames) {
const NodesModifierBakeFile &meta_file = *file_by_frame.lookup(frame);
auto frame_cache = std::make_unique<bake::FrameCache>();
frame_cache->frame = frame;
frame_cache->meta_data_source = meta_file.data();
bake_cache.frames.append(std::move(frame_cache));
}
bake_cache.memory_blob_reader = std::make_unique<bake::MemoryBlobReader>();
for (const NodesModifierBakeFile &blob_file :
Span{bake.packed->blob_files, bake.packed->blob_files_num})
{
bake_cache.memory_blob_reader->add(blob_file.name, blob_file.data());
}
bake_cache.blob_sharing = std::make_unique<bake::BlobReadSharing>();
return true;
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(bmain, object, nmd, id);
if (!bake_path) {
return false;
}
Vector<bake::MetaFile> meta_files = bake::find_sorted_meta_files(bake_path->meta_dir);
if (meta_files.is_empty()) {
return false;
}
bake_cache.reset();
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
for (const bake::MetaFile &meta_file : meta_files) {
auto frame_cache = std::make_unique<bake::FrameCache>();
frame_cache->frame = meta_file.frame;
frame_cache->meta_data_source = meta_file.path;
bake_cache.frames.append(std::move(frame_cache));
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
}
bake_cache.blobs_dir = bake_path->blobs_dir;
bake_cache.blob_sharing = std::make_unique<bake::BlobReadSharing>();
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
return true;
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
private:
static constexpr float max_delta_frames = 1.0f;
const NodesModifierData &nmd_;
const ModifierEvalContext &ctx_;
const Main *bmain_;
const Scene *scene_;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
SubFrame current_frame_;
bool use_frame_cache_;
bool depsgraph_is_active_;
bake::ModifierCache *modifier_cache_;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
float fps_;
bool has_invalid_simulation_ = false;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
public:
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
struct DataPerZone {
nodes::SimulationZoneBehavior behavior;
NodesModifierBakeDataBlockMap data_block_map;
};
mutable Map<int, std::unique_ptr<DataPerZone>> data_by_zone_id;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
NodesModifierSimulationParams(NodesModifierData &nmd, const ModifierEvalContext &ctx)
: nmd_(nmd), ctx_(ctx)
{
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
const Depsgraph *depsgraph = ctx_.depsgraph;
bmain_ = DEG_get_bmain(depsgraph);
current_frame_ = DEG_get_ctime(depsgraph);
const Scene *scene = DEG_get_input_scene(depsgraph);
scene_ = scene;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
use_frame_cache_ = ctx_.object->flag & OB_FLAG_USE_SIMULATION_CACHE;
depsgraph_is_active_ = DEG_is_active(depsgraph);
modifier_cache_ = nmd.runtime->cache.get();
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
fps_ = FPS;
if (!modifier_cache_) {
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return;
}
std::lock_guard lock{modifier_cache_->mutex};
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
if (depsgraph_is_active_) {
/* Invalidate data on user edits. */
if (nmd.modifier.flag & eModifierFlag_UserModified) {
for (std::unique_ptr<bake::SimulationNodeCache> &node_cache :
modifier_cache_->simulation_cache_by_id.values())
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
{
if (node_cache->cache_status != bake::CacheStatus::Baked) {
node_cache->cache_status = bake::CacheStatus::Invalid;
if (!node_cache->bake.frames.is_empty()) {
if (node_cache->bake.frames.last()->frame == current_frame_) {
/* Remove the last (which is the current) cached frame so that it is simulated
* again. */
node_cache->bake.frames.pop_last();
}
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
}
}
this->reset_invalid_node_bakes();
}
for (const std::unique_ptr<bake::SimulationNodeCache> &node_cache_ptr :
modifier_cache_->simulation_cache_by_id.values())
{
const bake::SimulationNodeCache &node_cache = *node_cache_ptr;
if (node_cache.cache_status == bake::CacheStatus::Invalid) {
has_invalid_simulation_ = true;
break;
}
}
}
void reset_invalid_node_bakes()
{
for (auto item : modifier_cache_->simulation_cache_by_id.items()) {
const int id = item.key;
bake::SimulationNodeCache &node_cache = *item.value;
if (node_cache.cache_status != bake::CacheStatus::Invalid) {
continue;
}
const std::optional<IndexRange> sim_frame_range = bake::get_node_bake_frame_range(
*scene_, *ctx_.object, nmd_, id);
if (!sim_frame_range.has_value()) {
continue;
}
const SubFrame start_frame{int(sim_frame_range->start())};
if (current_frame_ <= start_frame) {
node_cache.reset();
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
if (!node_cache.bake.frames.is_empty() &&
current_frame_ < node_cache.bake.frames.first()->frame)
{
node_cache.reset();
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
}
}
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
nodes::SimulationZoneBehavior *get(const int zone_id) const override
{
if (!modifier_cache_) {
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return nullptr;
}
std::lock_guard lock{modifier_cache_->mutex};
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
return &this->data_by_zone_id
.lookup_or_add_cb(zone_id,
[&]() {
auto data = std::make_unique<DataPerZone>();
data->behavior.data_block_map = &data->data_block_map;
this->init_simulation_info(
zone_id, data->behavior, data->data_block_map);
return data;
})
->behavior;
}
void init_simulation_info(const int zone_id,
nodes::SimulationZoneBehavior &zone_behavior,
NodesModifierBakeDataBlockMap &data_block_map) const
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
{
bake::SimulationNodeCache &node_cache =
*modifier_cache_->simulation_cache_by_id.lookup_or_add_cb(
zone_id, []() { return std::make_unique<bake::SimulationNodeCache>(); });
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
const NodesModifierBake &bake = *nmd_.find_bake(zone_id);
const IndexRange sim_frame_range = *bake::get_node_bake_frame_range(
*scene_, *ctx_.object, nmd_, zone_id);
const SubFrame sim_start_frame{int(sim_frame_range.first())};
const SubFrame sim_end_frame{int(sim_frame_range.last())};
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
if (!modifier_cache_->requested_bakes.contains(zone_id)) {
/* Try load baked data. */
if (!node_cache.bake.failed_finding_bake) {
if (node_cache.cache_status != bake::CacheStatus::Baked) {
if (try_find_baked_data(bake, node_cache.bake, *bmain_, *ctx_.object, nmd_, zone_id)) {
node_cache.cache_status = bake::CacheStatus::Baked;
}
else {
node_cache.bake.failed_finding_bake = true;
}
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
}
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
/* If there are no baked frames, we don't need keep track of the data-blocks. */
if (!node_cache.bake.frames.is_empty() || node_cache.prev_cache.has_value()) {
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
for (const NodesModifierDataBlock &data_block : Span{bake.data_blocks, bake.data_blocks_num})
{
data_block_map.old_mappings.add(data_block, data_block.id);
}
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
const BakeFrameIndices frame_indices = get_bake_frame_indices(node_cache.bake.frames,
current_frame_);
if (node_cache.cache_status == bake::CacheStatus::Baked) {
this->read_from_cache(frame_indices, node_cache, zone_behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return;
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
if (use_frame_cache_) {
/* If the depsgraph is active, we allow creating new simulation states. Otherwise, the access
* is read-only. */
if (depsgraph_is_active_) {
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
if (node_cache.bake.frames.is_empty()) {
if (current_frame_ < sim_start_frame || current_frame_ > sim_end_frame) {
/* Outside of simulation frame range, so ignore the simulation if there is no cache. */
this->input_pass_through(zone_behavior);
this->output_pass_through(zone_behavior);
return;
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
/* Initialize the simulation. */
if (current_frame_ > sim_start_frame || has_invalid_simulation_) {
node_cache.cache_status = bake::CacheStatus::Invalid;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
this->input_pass_through(zone_behavior);
this->output_store_frame_cache(node_cache, zone_behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return;
}
if (frame_indices.prev && !frame_indices.current && !frame_indices.next &&
current_frame_ <= sim_end_frame)
{
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
/* Read the previous frame's data and store the newly computed simulation state. */
auto &output_copy_info = zone_behavior.input.emplace<sim_input::OutputCopy>();
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
const bake::FrameCache &prev_frame_cache = *node_cache.bake.frames[*frame_indices.prev];
const float real_delta_frames = float(current_frame_) - float(prev_frame_cache.frame);
if (real_delta_frames != 1) {
node_cache.cache_status = bake::CacheStatus::Invalid;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
const float delta_frames = std::min(max_delta_frames, real_delta_frames);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
output_copy_info.delta_time = delta_frames / fps_;
output_copy_info.state = prev_frame_cache.state;
this->output_store_frame_cache(node_cache, zone_behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return;
}
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
}
this->read_from_cache(frame_indices, node_cache, zone_behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return;
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
/* When there is no per-frame cache, check if there is a previous state. */
if (node_cache.prev_cache) {
if (node_cache.prev_cache->frame < current_frame_) {
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
/* Do a simulation step. */
const float delta_frames = std::min(
max_delta_frames, float(current_frame_) - float(node_cache.prev_cache->frame));
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
auto &output_move_info = zone_behavior.input.emplace<sim_input::OutputMove>();
output_move_info.delta_time = delta_frames / fps_;
output_move_info.state = std::move(node_cache.prev_cache->state);
this->store_as_prev_items(node_cache, zone_behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return;
}
if (node_cache.prev_cache->frame == current_frame_) {
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
/* Just read from the previous state if the frame has not changed. */
auto &output_copy_info = zone_behavior.input.emplace<sim_input::OutputCopy>();
output_copy_info.delta_time = 0.0f;
output_copy_info.state = node_cache.prev_cache->state;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
auto &read_single_info = zone_behavior.output.emplace<sim_output::ReadSingle>();
read_single_info.state = node_cache.prev_cache->state;
return;
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
if (!depsgraph_is_active_) {
/* There is no previous state, and it's not possible to initialize the simulation because
* the depsgraph is not active. */
zone_behavior.input.emplace<sim_input::PassThrough>();
zone_behavior.output.emplace<sim_output::PassThrough>();
return;
}
/* Reset the simulation when the scene time moved backwards. */
node_cache.prev_cache.reset();
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
zone_behavior.input.emplace<sim_input::PassThrough>();
if (depsgraph_is_active_) {
/* Initialize the simulation. */
this->store_as_prev_items(node_cache, zone_behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
else {
zone_behavior.output.emplace<sim_output::PassThrough>();
}
}
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
void input_pass_through(nodes::SimulationZoneBehavior &zone_behavior) const
{
zone_behavior.input.emplace<sim_input::PassThrough>();
}
void output_pass_through(nodes::SimulationZoneBehavior &zone_behavior) const
{
zone_behavior.output.emplace<sim_output::PassThrough>();
}
void output_store_frame_cache(bake::SimulationNodeCache &node_cache,
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
nodes::SimulationZoneBehavior &zone_behavior) const
{
auto &store_new_state_info = zone_behavior.output.emplace<sim_output::StoreNewState>();
store_new_state_info.store_fn = [simulation_cache = modifier_cache_,
node_cache = &node_cache,
current_frame = current_frame_](bke::bake::BakeState state) {
std::lock_guard lock{simulation_cache->mutex};
auto frame_cache = std::make_unique<bake::FrameCache>();
frame_cache->frame = current_frame;
frame_cache->state = std::move(state);
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
node_cache->bake.frames.append(std::move(frame_cache));
};
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
void store_as_prev_items(bake::SimulationNodeCache &node_cache,
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
nodes::SimulationZoneBehavior &zone_behavior) const
{
auto &store_new_state_info = zone_behavior.output.emplace<sim_output::StoreNewState>();
store_new_state_info.store_fn = [simulation_cache = modifier_cache_,
node_cache = &node_cache,
current_frame = current_frame_](bke::bake::BakeState state) {
std::lock_guard lock{simulation_cache->mutex};
if (!node_cache->prev_cache) {
node_cache->prev_cache.emplace();
}
node_cache->prev_cache->state = std::move(state);
node_cache->prev_cache->frame = current_frame;
};
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
void read_from_cache(const BakeFrameIndices &frame_indices,
bake::SimulationNodeCache &node_cache,
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
nodes::SimulationZoneBehavior &zone_behavior) const
{
if (frame_indices.prev) {
auto &output_copy_info = zone_behavior.input.emplace<sim_input::OutputCopy>();
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
bake::FrameCache &frame_cache = *node_cache.bake.frames[*frame_indices.prev];
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
const float delta_frames = std::min(max_delta_frames,
float(current_frame_) - float(frame_cache.frame));
output_copy_info.delta_time = delta_frames / fps_;
output_copy_info.state = frame_cache.state;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
else {
zone_behavior.input.emplace<sim_input::PassThrough>();
}
if (frame_indices.current) {
this->read_single(*frame_indices.current, node_cache, zone_behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
else if (frame_indices.next) {
if (frame_indices.prev) {
this->read_interpolated(
*frame_indices.prev, *frame_indices.next, node_cache, zone_behavior);
}
else {
this->output_pass_through(zone_behavior);
}
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
else if (frame_indices.prev) {
this->read_single(*frame_indices.prev, node_cache, zone_behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
else {
this->output_pass_through(zone_behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
void read_single(const int frame_index,
bake::SimulationNodeCache &node_cache,
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
nodes::SimulationZoneBehavior &zone_behavior) const
{
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
bake::FrameCache &frame_cache = *node_cache.bake.frames[frame_index];
ensure_bake_loaded(node_cache.bake, frame_cache);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
auto &read_single_info = zone_behavior.output.emplace<sim_output::ReadSingle>();
read_single_info.state = frame_cache.state;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
void read_interpolated(const int prev_frame_index,
const int next_frame_index,
bake::SimulationNodeCache &node_cache,
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
nodes::SimulationZoneBehavior &zone_behavior) const
{
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
bake::FrameCache &prev_frame_cache = *node_cache.bake.frames[prev_frame_index];
bake::FrameCache &next_frame_cache = *node_cache.bake.frames[next_frame_index];
ensure_bake_loaded(node_cache.bake, prev_frame_cache);
ensure_bake_loaded(node_cache.bake, next_frame_cache);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
auto &read_interpolated_info = zone_behavior.output.emplace<sim_output::ReadInterpolated>();
read_interpolated_info.mix_factor = (float(current_frame_) - float(prev_frame_cache.frame)) /
(float(next_frame_cache.frame) -
float(prev_frame_cache.frame));
read_interpolated_info.prev_state = prev_frame_cache.state;
read_interpolated_info.next_state = next_frame_cache.state;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
};
class NodesModifierBakeParams : public nodes::GeoNodesBakeParams {
private:
const NodesModifierData &nmd_;
const ModifierEvalContext &ctx_;
Main *bmain_;
SubFrame current_frame_;
bake::ModifierCache *modifier_cache_;
bool depsgraph_is_active_;
public:
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
struct DataPerNode {
nodes::BakeNodeBehavior behavior;
NodesModifierBakeDataBlockMap data_block_map;
};
mutable Map<int, std::unique_ptr<DataPerNode>> data_by_node_id;
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
NodesModifierBakeParams(NodesModifierData &nmd, const ModifierEvalContext &ctx)
: nmd_(nmd), ctx_(ctx)
{
const Depsgraph *depsgraph = ctx_.depsgraph;
current_frame_ = DEG_get_ctime(depsgraph);
modifier_cache_ = nmd.runtime->cache.get();
depsgraph_is_active_ = DEG_is_active(depsgraph);
bmain_ = DEG_get_bmain(depsgraph);
}
nodes::BakeNodeBehavior *get(const int id) const override
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
{
if (!modifier_cache_) {
return nullptr;
}
std::lock_guard lock{modifier_cache_->mutex};
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
return &this->data_by_node_id
.lookup_or_add_cb(id,
[&]() {
auto data = std::make_unique<DataPerNode>();
data->behavior.data_block_map = &data->data_block_map;
this->init_bake_behavior(
id, data->behavior, data->data_block_map);
return data;
})
->behavior;
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
return nullptr;
}
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
private:
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
void init_bake_behavior(const int id,
nodes::BakeNodeBehavior &behavior,
NodesModifierBakeDataBlockMap &data_block_map) const
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
{
bake::BakeNodeCache &node_cache = *modifier_cache_->bake_cache_by_id.lookup_or_add_cb(
id, []() { return std::make_unique<bake::BakeNodeCache>(); });
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
const NodesModifierBake &bake = *nmd_.find_bake(id);
for (const NodesModifierDataBlock &data_block : Span{bake.data_blocks, bake.data_blocks_num}) {
data_block_map.old_mappings.add(data_block, data_block.id);
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
if (depsgraph_is_active_) {
if (modifier_cache_->requested_bakes.contains(id)) {
/* This node is baked during the current evaluation. */
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
auto &store_info = behavior.behavior.emplace<sim_output::StoreNewState>();
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
store_info.store_fn = [modifier_cache = modifier_cache_,
node_cache = &node_cache,
current_frame = current_frame_](bake::BakeState state) {
std::lock_guard lock{modifier_cache->mutex};
auto frame_cache = std::make_unique<bake::FrameCache>();
frame_cache->frame = current_frame;
frame_cache->state = std::move(state);
auto &frames = node_cache->bake.frames;
const int insert_index = binary_search::first_if(
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
frames, [&](const std::unique_ptr<bake::FrameCache> &frame_cache) {
return frame_cache->frame > current_frame;
});
frames.insert(insert_index, std::move(frame_cache));
};
return;
}
}
/* Try load baked data. */
if (node_cache.bake.frames.is_empty()) {
if (!node_cache.bake.failed_finding_bake) {
if (!try_find_baked_data(bake, node_cache.bake, *bmain_, *ctx_.object, nmd_, id)) {
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
node_cache.bake.failed_finding_bake = true;
}
}
}
if (node_cache.bake.frames.is_empty()) {
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
behavior.behavior.emplace<sim_output::PassThrough>();
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
return;
}
const BakeFrameIndices frame_indices = get_bake_frame_indices(node_cache.bake.frames,
current_frame_);
if (frame_indices.current) {
this->read_single(*frame_indices.current, node_cache, behavior);
return;
}
if (frame_indices.prev && frame_indices.next) {
this->read_interpolated(*frame_indices.prev, *frame_indices.next, node_cache, behavior);
return;
}
if (frame_indices.prev) {
this->read_single(*frame_indices.prev, node_cache, behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return;
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
if (frame_indices.next) {
this->read_single(*frame_indices.next, node_cache, behavior);
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return;
}
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
BLI_assert_unreachable();
}
void read_single(const int frame_index,
bake::BakeNodeCache &node_cache,
nodes::BakeNodeBehavior &behavior) const
{
bake::FrameCache &frame_cache = *node_cache.bake.frames[frame_index];
ensure_bake_loaded(node_cache.bake, frame_cache);
if (this->check_read_error(frame_cache, behavior)) {
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
return;
}
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
auto &read_single_info = behavior.behavior.emplace<sim_output::ReadSingle>();
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
read_single_info.state = frame_cache.state;
}
void read_interpolated(const int prev_frame_index,
const int next_frame_index,
bake::BakeNodeCache &node_cache,
nodes::BakeNodeBehavior &behavior) const
{
bake::FrameCache &prev_frame_cache = *node_cache.bake.frames[prev_frame_index];
bake::FrameCache &next_frame_cache = *node_cache.bake.frames[next_frame_index];
ensure_bake_loaded(node_cache.bake, prev_frame_cache);
ensure_bake_loaded(node_cache.bake, next_frame_cache);
if (this->check_read_error(prev_frame_cache, behavior) ||
this->check_read_error(next_frame_cache, behavior))
{
return;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
auto &read_interpolated_info = behavior.behavior.emplace<sim_output::ReadInterpolated>();
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
read_interpolated_info.mix_factor = (float(current_frame_) - float(prev_frame_cache.frame)) /
(float(next_frame_cache.frame) -
float(prev_frame_cache.frame));
read_interpolated_info.prev_state = prev_frame_cache.state;
read_interpolated_info.next_state = next_frame_cache.state;
}
[[nodiscard]] bool check_read_error(const bake::FrameCache &frame_cache,
nodes::BakeNodeBehavior &behavior) const
{
if (frame_cache.meta_data_source && frame_cache.state.items_by_id.is_empty()) {
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
auto &read_error_info = behavior.behavior.emplace<sim_output::ReadError>();
read_error_info.message = RPT_("Cannot load the baked data");
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
return true;
}
return false;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
}
};
Geometry Nodes: add simulation support This adds support for building simulations with geometry nodes. A new `Simulation Input` and `Simulation Output` node allow maintaining a simulation state across multiple frames. Together these two nodes form a `simulation zone` which contains all the nodes that update the simulation state from one frame to the next. A new simulation zone can be added via the menu (`Simulation > Simulation Zone`) or with the node add search. The simulation state contains a geometry by default. However, it is possible to add multiple geometry sockets as well as other socket types. Currently, field inputs are evaluated and stored for the preceding geometry socket in the order that the sockets are shown. Simulation state items can be added by linking one of the empty sockets to something else. In the sidebar, there is a new panel that allows adding, removing and reordering these sockets. The simulation nodes behave as follows: * On the first frame, the inputs of the `Simulation Input` node are evaluated to initialize the simulation state. In later frames these sockets are not evaluated anymore. The `Delta Time` at the first frame is zero, but the simulation zone is still evaluated. * On every next frame, the `Simulation Input` node outputs the simulation state of the previous frame. Nodes in the simulation zone can edit that data in arbitrary ways, also taking into account the `Delta Time`. The new simulation state has to be passed to the `Simulation Output` node where it is cached and forwarded. * On a frame that is already cached or baked, the nodes in the simulation zone are not evaluated, because the `Simulation Output` node can return the previously cached data directly. It is not allowed to connect sockets from inside the simulation zone to the outside without going through the `Simulation Output` node. This is a necessary restriction to make caching and sub-frame interpolation work. Links can go into the simulation zone without problems though. Anonymous attributes are not propagated by the simulation nodes unless they are explicitly stored in the simulation state. This is unfortunate, but currently there is no practical and reliable alternative. The core problem is detecting which anonymous attributes will be required for the simulation and afterwards. While we can detect this for the current evaluation, we can't look into the future in time to see what data will be necessary. We intend to make it easier to explicitly pass data through a simulation in the future, even if the simulation is in a nested node group. There is a new `Simulation Nodes` panel in the physics tab in the properties editor. It allows baking all simulation zones on the selected objects. The baking options are intentially kept at a minimum for this MVP. More features for simulation baking as well as baking in general can be expected to be added separately. All baked data is stored on disk in a folder next to the .blend file. #106937 describes how baking is implemented in more detail. Volumes can not be baked yet and materials are lost during baking for now. Packing the baked data into the .blend file is not yet supported. The timeline indicates which frames are currently cached, baked or cached but invalidated by user-changes. Simulation input and output nodes are internally linked together by their `bNode.identifier` which stays the same even if the node name changes. They are generally added and removed together. However, there are still cases where "dangling" simulation nodes can be created currently. Those generally don't cause harm, but would be nice to avoid this in more cases in the future. Co-authored-by: Hans Goudey <h.goudey@me.com> Co-authored-by: Lukas Tönne <lukas@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
static void add_missing_data_block_mappings(
NodesModifierBake &bake,
const Span<bake::BakeDataBlockID> missing,
FunctionRef<ID *(const bake::BakeDataBlockID &)> get_data_block)
{
const int old_num = bake.data_blocks_num;
const int new_num = old_num + missing.size();
bake.data_blocks = reinterpret_cast<NodesModifierDataBlock *>(
MEM_recallocN(bake.data_blocks, sizeof(NodesModifierDataBlock) * new_num));
for (const int i : missing.index_range()) {
NodesModifierDataBlock &data_block = bake.data_blocks[old_num + i];
const blender::bke::bake::BakeDataBlockID &key = missing[i];
data_block.id_name = BLI_strdup(key.id_name.c_str());
if (!key.lib_name.empty()) {
data_block.lib_name = BLI_strdup(key.lib_name.c_str());
}
data_block.id_type = int(key.type);
ID *id = get_data_block(key);
if (id) {
data_block.id = id;
}
}
bake.data_blocks_num = new_num;
}
void nodes_modifier_data_block_destruct(NodesModifierDataBlock *data_block, const bool do_id_user)
{
MEM_SAFE_FREE(data_block->id_name);
MEM_SAFE_FREE(data_block->lib_name);
if (do_id_user) {
id_us_min(data_block->id);
}
}
/**
* During evaluation we might have baked geometry that contains references to other data-blocks
* (such as materials). We need to make sure that those data-blocks stay dependencies of the
* modifier. Otherwise, the data-block references might not work when the baked data is loaded
* again. Therefor, the dependencies are written back to the original modifier.
*/
static void add_data_block_items_writeback(const ModifierEvalContext &ctx,
NodesModifierData &nmd_eval,
NodesModifierData &nmd_orig,
NodesModifierSimulationParams &simulation_params,
NodesModifierBakeParams &bake_params)
{
Depsgraph *depsgraph = ctx.depsgraph;
Main *bmain = DEG_get_bmain(depsgraph);
struct DataPerBake {
bool reset_first = false;
Map<bake::BakeDataBlockID, ID *> new_mappings;
};
Map<int, DataPerBake> writeback_data;
for (auto item : simulation_params.data_by_zone_id.items()) {
DataPerBake data;
NodesModifierBake &bake = *nmd_eval.find_bake(item.key);
if (item.value->data_block_map.old_mappings.size() < bake.data_blocks_num) {
data.reset_first = true;
}
if (bake::SimulationNodeCache *node_cache = nmd_eval.runtime->cache->get_simulation_node_cache(
item.key))
{
/* Only writeback if the bake node has actually baked anything. */
if (!node_cache->bake.frames.is_empty() || node_cache->prev_cache.has_value()) {
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
data.new_mappings = std::move(item.value->data_block_map.new_mappings);
}
}
if (data.reset_first || !data.new_mappings.is_empty()) {
writeback_data.add(item.key, std::move(data));
}
}
for (auto item : bake_params.data_by_node_id.items()) {
if (bake::BakeNodeCache *node_cache = nmd_eval.runtime->cache->get_bake_node_cache(item.key)) {
/* Only writeback if the bake node has actually baked anything. */
if (!node_cache->bake.frames.is_empty()) {
DataPerBake data;
data.new_mappings = std::move(item.value->data_block_map.new_mappings);
writeback_data.add(item.key, std::move(data));
}
}
}
if (writeback_data.is_empty()) {
/* Nothing to do. */
return;
}
deg::sync_writeback::add(
*depsgraph,
[object_eval = ctx.object,
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
bmain,
&nmd_orig,
&nmd_eval,
writeback_data = std::move(writeback_data)]() {
for (auto item : writeback_data.items()) {
const int bake_id = item.key;
DataPerBake data = item.value;
NodesModifierBake &bake_orig = *nmd_orig.find_bake(bake_id);
NodesModifierBake &bake_eval = *nmd_eval.find_bake(bake_id);
if (data.reset_first) {
/* Reset data-block list on original data. */
dna::array::clear<NodesModifierDataBlock>(&bake_orig.data_blocks,
&bake_orig.data_blocks_num,
&bake_orig.active_data_block,
[](NodesModifierDataBlock *data_block) {
nodes_modifier_data_block_destruct(
data_block, true);
});
/* Reset data-block list on evaluated data. */
dna::array::clear<NodesModifierDataBlock>(&bake_eval.data_blocks,
&bake_eval.data_blocks_num,
&bake_eval.active_data_block,
[](NodesModifierDataBlock *data_block) {
nodes_modifier_data_block_destruct(
data_block, false);
});
}
Vector<bake::BakeDataBlockID> sorted_new_mappings;
sorted_new_mappings.extend(data.new_mappings.keys().begin(),
data.new_mappings.keys().end());
bool needs_reevaluation = false;
/* Add new data block mappings to the original modifier. This may do a name lookup in
* bmain to find the data block if there is not faster way to get it. */
add_missing_data_block_mappings(
bake_orig, sorted_new_mappings, [&](const bake::BakeDataBlockID &key) -> ID * {
ID *id_orig = nullptr;
if (ID *id_eval = data.new_mappings.lookup_default(key, nullptr)) {
id_orig = DEG_get_original(id_eval);
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
}
else {
needs_reevaluation = true;
id_orig = BKE_libblock_find_name_and_library(
bmain, short(key.type), key.id_name.c_str(), key.lib_name.c_str());
}
if (id_orig) {
id_us_plus(id_orig);
}
return id_orig;
});
/* Add new data block mappings to the evaluated modifier. In most cases this makes it so
* the evaluated modifier is in the same state as if it were copied from the updated
* original again. The exception is when a missing data block was found that is not in
* the depsgraph currently. */
add_missing_data_block_mappings(
bake_eval, sorted_new_mappings, [&](const bake::BakeDataBlockID &key) -> ID * {
return data.new_mappings.lookup_default(key, nullptr);
});
if (needs_reevaluation) {
Object *object_orig = DEG_get_original(object_eval);
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
DEG_id_tag_update(&object_orig->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(bmain);
}
}
});
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
static void modifyGeometry(ModifierData *md,
const ModifierEvalContext *ctx,
bke::GeometrySet &geometry_set)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
using namespace blender;
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->node_group == nullptr) {
return;
}
NodesModifierData *nmd_orig = reinterpret_cast<NodesModifierData *>(
BKE_modifier_get_original(ctx->object, &nmd->modifier));
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
nodes::PropertiesVectorSet properties = nodes::build_properties_vector_set(
nmd->settings.properties);
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
const bNodeTree &tree = *nmd->node_group;
check_property_socket_sync(ctx->object, properties, md);
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
tree.ensure_topology_cache();
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
const bNode *output_node = tree.group_output_node();
if (output_node == nullptr) {
BKE_modifier_set_error(ctx->object, md, "Node group must have a group output node");
geometry_set.clear();
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
return;
}
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
Span<const bNodeSocket *> group_outputs = output_node->input_sockets().drop_back(1);
if (group_outputs.is_empty()) {
BKE_modifier_set_error(ctx->object, md, "Node group must have an output socket");
geometry_set.clear();
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
return;
}
const bNodeSocket *first_output_socket = group_outputs[0];
if (!STREQ(first_output_socket->idname, "NodeSocketGeometry")) {
BKE_modifier_set_error(ctx->object, md, "Node group's first output must be a geometry");
geometry_set.clear();
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
return;
}
const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
nodes::ensure_geometry_nodes_lazy_function_graph(tree);
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
if (lf_graph_info == nullptr) {
BKE_modifier_set_error(ctx->object, md, "Cannot evaluate node group");
geometry_set.clear();
return;
}
bool use_orig_index_verts = false;
bool use_orig_index_edges = false;
bool use_orig_index_faces = false;
if (const Mesh *mesh = geometry_set.get_mesh()) {
use_orig_index_verts = CustomData_has_layer(&mesh->vert_data, CD_ORIGINDEX);
use_orig_index_edges = CustomData_has_layer(&mesh->edge_data, CD_ORIGINDEX);
use_orig_index_faces = CustomData_has_layer(&mesh->face_data, CD_ORIGINDEX);
}
nodes::GeoNodesCallData call_data;
nodes::GeoNodesModifierData modifier_eval_data{};
modifier_eval_data.depsgraph = ctx->depsgraph;
modifier_eval_data.self_object = ctx->object;
auto eval_log = std::make_unique<geo_log::GeoModifierLog>();
call_data.modifier_data = &modifier_eval_data;
Geometry Nodes: refactor simulation storage and how simulation nodes access it Goals of the refactor: * Internal support for baking individual simulation zones (not exposed in the UI yet). * More well-defined access to simulation data in geometry nodes. Especially, it should be more obvious where data is modified. A similar approach should also work for the Bake node. Previously, there were a bunch of simulation specific properties in `GeoNodesModifierData` and then the simulation input and output nodes would have to figure out what to do with that data. Now, there is a new `GeoNodesSimulationParams` which controls the behavior of simulation zones. Contrary to before, different simulation zones can now be handled independently, even if that is not really used yet. `GeoNodesSimulationParams` has to be subclassed by a user of the geometry nodes API. The subclass controls what each simulation input and output node does. This some of the logic that was part of the node before, into the modifier. The way we store simulation data is "transposed". Previously, we stored zone data per frame, but now we store frame data per zone. This allows different zones to be more independent. Consequently, the way the simulation cache is accessed changed. I kept things simpler for now, avoiding many of the methods we had before, and directly accessing the data more often which is often simple enough. This change also makes it theoretically possible to store baked data for separate zones independently. A downside of this is, that existing baked data can't be read anymore. We don't really have compatibility guarantees for this format yet, so it's ok. Users will have to bake again. The bake folder for the modifier now contains an extra subfolder for every zone. Drawing the cached/baked frames in the timeline is less straight forward now. Currently, it just draws the state of one of the zones, which usually is identical to that of all other zones. This will change in the future though, and then the timeline drawing also needs some new UI work. Pull Request: https://projects.blender.org/blender/blender/pulls/111623
2023-08-31 16:28:03 +02:00
NodesModifierSimulationParams simulation_params(*nmd, *ctx);
call_data.simulation_params = &simulation_params;
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
NodesModifierBakeParams bake_params{*nmd, *ctx};
call_data.bake_params = &bake_params;
Set<ComputeContextHash> socket_log_contexts;
if (logging_enabled(ctx)) {
call_data.eval_log = eval_log.get();
find_socket_log_contexts(*nmd, *ctx, socket_log_contexts);
call_data.socket_log_contexts = &socket_log_contexts;
}
nodes::GeoNodesSideEffectNodes side_effect_nodes;
Geometry Nodes: support attaching gizmos to input values This adds support for attaching gizmos for input values. The goal is to make it easier for users to set input values intuitively in the 3D viewport. We went through multiple different possible designs until we settled on the one implemented here. We picked it for it's flexibility and ease of use when using geometry node assets. The core principle in the design is that **gizmos are attached to existing input values instead of being the input value themselves**. This actually fits the existing concept of gizmos in Blender well, but may be a bit unintutitive in a node setup at first. The attachment is done using links in the node editor. The most basic usage of the node is to link a Value node to the new Linear Gizmo node. This attaches the gizmo to the input value and allows you to change it from the 3D view. The attachment is indicated by the gizmo icon in the sockets which are controlled by a gizmo as well as the back-link (notice the double link) when the gizmo is active. The core principle makes it straight forward to control the same node setup from the 3D view with gizmos, or by manually changing input values, or by driving the input values procedurally. If the input value is controlled indirectly by other inputs, it's often possible to **automatically propagate** the gizmo to the actual input. Backpropagation does not work for all nodes, although more nodes can be supported over time. This patch adds the first three gizmo nodes which cover common use cases: * **Linear Gizmo**: Creates a gizmo that controls a float or integer value using a linear movement of e.g. an arrow in the 3D viewport. * **Dial Gizmo**: Creates a circular gizmo in the 3D viewport that can be rotated to change the attached angle input. * **Transform Gizmo**: Creates a simple gizmo for location, rotation and scale. In the future, more built-in gizmos and potentially the ability for custom gizmos could be added. All gizmo nodes have a **Transform** geometry output. Using it is optional but it is recommended when the gizmo is used to control inputs that affect a geometry. When it is used, Blender will automatically transform the gizmos together with the geometry that they control. To achieve this, the output should be merged with the generated geometry using the *Join Geometry* node. The data contained in *Transform* output is not visible geometry, but just internal information that helps Blender to give a better user experience when using gizmos. The gizmo nodes have a multi-input socket. This allows **controlling multiple values** with the same gizmo. Only a small set of **gizmo shapes** is supported initially. It might be extended in the future but one goal is to give the gizmos used by different node group assets a familiar look and feel. A similar constraint exists for **colors**. Currently, one can choose from a fixed set of colors which can be modified in the theme settings. The set of **visible gizmos** is determined by a multiple factors because it's not really feasible to show all possible gizmos at all times. To see any of the geometry nodes gizmos, the "Active Modifier" option has to be enabled in the "Viewport Gizmos" popover. Then all gizmos are drawn for which at least one of the following is true: * The gizmo controls an input of the active modifier of the active object. * The gizmo controls a value in a selected node in an open node editor. * The gizmo controls a pinned value in an open node editor. Pinning works by clicking the gizmo icon next to the value. Pull Request: https://projects.blender.org/blender/blender/pulls/112677
2024-07-10 16:18:47 +02:00
find_side_effect_nodes(*nmd, *ctx, side_effect_nodes, socket_log_contexts);
call_data.side_effect_nodes = &side_effect_nodes;
bke::ModifierComputeContext modifier_compute_context{nullptr, *nmd};
geometry_set = nodes::execute_geometry_nodes_on_geometry(
tree, properties, modifier_compute_context, call_data, std::move(geometry_set));
if (logging_enabled(ctx)) {
nmd_orig->runtime->eval_log = std::move(eval_log);
}
if (DEG_is_active(ctx->depsgraph) && !(ctx->flag & MOD_APPLY_TO_ORIGINAL)) {
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
add_data_block_items_writeback(*ctx, *nmd, *nmd_orig, simulation_params, bake_params);
}
if (use_orig_index_verts || use_orig_index_edges || use_orig_index_faces) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
/* Add #CD_ORIGINDEX layers if they don't exist already. This is required because the
* #eModifierTypeFlag_SupportsMapping flag is set. If the layers did not exist before, it is
* assumed that the output mesh does not have a mapping to the original mesh. */
if (use_orig_index_verts) {
CustomData_add_layer(&mesh->vert_data, CD_ORIGINDEX, CD_SET_DEFAULT, mesh->verts_num);
}
if (use_orig_index_edges) {
CustomData_add_layer(&mesh->edge_data, CD_ORIGINDEX, CD_SET_DEFAULT, mesh->edges_num);
}
if (use_orig_index_faces) {
CustomData_add_layer(&mesh->face_data, CD_ORIGINDEX, CD_SET_DEFAULT, mesh->faces_num);
}
}
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
bke::GeometrySet geometry_set = bke::GeometrySet::from_mesh(
mesh, bke::GeometryOwnershipType::Editable);
modifyGeometry(md, ctx, geometry_set);
bke::MeshComponent &mesh_component = geometry_set.get_component_for_write<bke::MeshComponent>();
if (mesh_component.get() != mesh) {
/* If this is the same as the input mesh, it's not necessary to make a copy of it even if it's
* not owned by the geometry set. That's because we know that the caller manages the ownership
* of the mesh. */
mesh_component.ensure_owns_direct_data();
}
Mesh *new_mesh = mesh_component.release();
if (new_mesh == nullptr) {
return BKE_mesh_new_nomain(0, 0, 0, 0);
}
return new_mesh;
}
static void modify_geometry_set(ModifierData *md,
const ModifierEvalContext *ctx,
bke::GeometrySet *geometry_set)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
modifyGeometry(md, ctx, *geometry_set);
}
struct SocketSearchData {
uint32_t object_session_uid;
char modifier_name[MAX_NAME];
char socket_identifier[MAX_NAME];
bool is_output;
};
/* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */
BLI_STATIC_ASSERT(std::is_trivially_destructible_v<SocketSearchData>, "");
struct DrawGroupInputsContext {
const bContext &C;
NodesModifierData &nmd;
nodes::PropertiesVectorSet properties;
PointerRNA *md_ptr;
PointerRNA *bmain_ptr;
Array<bool> input_usages;
};
static NodesModifierData *get_modifier_data(Main &bmain,
const wmWindowManager &wm,
const SocketSearchData &data)
{
if (ED_screen_animation_playing(&wm)) {
/* Work around an issue where the attribute search exec function has stale pointers when data
* is reallocated when evaluating the node tree, causing a crash. This would be solved by
* allowing the UI search data to own arbitrary memory rather than just referencing it. */
return nullptr;
}
const Object *object = (Object *)BKE_libblock_find_session_uid(
&bmain, ID_OB, data.object_session_uid);
if (object == nullptr) {
return nullptr;
}
ModifierData *md = BKE_modifiers_findby_name(object, data.modifier_name);
if (md == nullptr) {
return nullptr;
}
BLI_assert(md->type == eModifierType_Nodes);
return reinterpret_cast<NodesModifierData *>(md);
}
static geo_log::GeoTreeLog *get_root_tree_log(const NodesModifierData &nmd)
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
{
if (!nmd.runtime->eval_log) {
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
return nullptr;
}
bke::ModifierComputeContext compute_context{nullptr, nmd};
return &nmd.runtime->eval_log->get_tree_log(compute_context.hash());
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
}
static void attribute_search_update_fn(
const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
{
SocketSearchData &data = *static_cast<SocketSearchData *>(arg);
const NodesModifierData *nmd = get_modifier_data(*CTX_data_main(C), *CTX_wm_manager(C), data);
if (nmd == nullptr) {
return;
}
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
if (nmd->node_group == nullptr) {
return;
}
geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd);
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
if (tree_log == nullptr) {
return;
}
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
tree_log->ensure_existing_attributes();
nmd->node_group->ensure_topology_cache();
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
Vector<const bNodeSocket *> sockets_to_check;
if (data.is_output) {
for (const bNode *node : nmd->node_group->nodes_by_type("NodeGroupOutput")) {
for (const bNodeSocket *socket : node->input_sockets()) {
if (socket->type == SOCK_GEOMETRY) {
sockets_to_check.append(socket);
}
}
}
}
else {
for (const bNode *node : nmd->node_group->group_input_nodes()) {
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
for (const bNodeSocket *socket : node->output_sockets()) {
if (socket->type == SOCK_GEOMETRY) {
sockets_to_check.append(socket);
}
}
}
}
Set<StringRef> names;
Vector<const geo_log::GeometryAttributeInfo *> attributes;
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
for (const bNodeSocket *socket : sockets_to_check) {
const geo_log::ValueLog *value_log = tree_log->find_socket_value_log(*socket);
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
if (value_log == nullptr) {
continue;
}
if (const auto *geo_log = dynamic_cast<const geo_log::GeometryInfoLog *>(value_log)) {
for (const geo_log::GeometryAttributeInfo &attribute : geo_log->attributes) {
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
if (names.add(attribute.name)) {
attributes.append(&attribute);
}
}
}
}
ui::attribute_search_add_items(str, data.is_output, attributes.as_span(), items, is_first);
}
static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
{
if (item_v == nullptr) {
return;
}
SocketSearchData &data = *static_cast<SocketSearchData *>(data_v);
const auto &item = *static_cast<const geo_log::GeometryAttributeInfo *>(item_v);
const NodesModifierData *nmd = get_modifier_data(*CTX_data_main(C), *CTX_wm_manager(C), data);
if (nmd == nullptr) {
return;
}
const std::string attribute_prop_name = data.socket_identifier +
nodes::input_attribute_name_suffix;
IDProperty &name_property = *IDP_GetPropertyFromGroup(nmd->settings.properties,
attribute_prop_name);
IDP_AssignString(&name_property, item.name.c_str());
ED_undo_push(C, "Assign Attribute Name");
}
static void add_attribute_search_button(DrawGroupInputsContext &ctx,
uiLayout *layout,
const StringRefNull rna_path_attribute_name,
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const bNodeTreeInterfaceSocket &socket,
const bool is_output)
{
if (!ctx.nmd.runtime->eval_log) {
layout->prop(ctx.md_ptr, rna_path_attribute_name, UI_ITEM_NONE, "", ICON_NONE);
return;
}
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but = uiDefIconTextButR(block,
UI_BTYPE_SEARCH_MENU,
0,
ICON_NONE,
"",
0,
0,
10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
UI_UNIT_Y,
ctx.md_ptr,
rna_path_attribute_name,
0,
0.0f,
0.0f,
StringRef(socket.description));
const Object *object = ed::object::context_object(&ctx.C);
BLI_assert(object != nullptr);
if (object == nullptr) {
return;
}
SocketSearchData *data = MEM_callocN<SocketSearchData>(__func__);
data->object_session_uid = object->id.session_uid;
STRNCPY(data->modifier_name, ctx.nmd.modifier.name);
STRNCPY(data->socket_identifier, socket.identifier);
data->is_output = is_output;
UI_but_func_search_set_results_are_suggestions(but, true);
UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP);
UI_but_func_search_set(but,
nullptr,
attribute_search_update_fn,
static_cast<void *>(data),
true,
nullptr,
attribute_search_exec_fn,
nullptr);
char *attribute_name = RNA_string_get_alloc(
ctx.md_ptr, rna_path_attribute_name.c_str(), nullptr, 0, nullptr);
const bool access_allowed = bke::allow_procedural_attribute_access(attribute_name);
MEM_freeN(attribute_name);
if (!access_allowed) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
}
static void add_attribute_search_or_value_buttons(DrawGroupInputsContext &ctx,
uiLayout *layout,
const StringRef socket_id_esc,
const StringRefNull rna_path,
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const bNodeTreeInterfaceSocket &socket)
{
const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
const std::string rna_path_attribute_name = fmt::format(
"[\"{}{}\"]", socket_id_esc, nodes::input_attribute_name_suffix);
/* We're handling this manually in this case. */
uiLayoutSetPropDecorate(layout, false);
uiLayout *split = &layout->split(0.4f, false);
uiLayout *name_row = &split->row(false);
uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT);
uiLayout *prop_row = nullptr;
const std::optional<StringRef> attribute_name = nodes::input_attribute_name_get(ctx.properties,
socket);
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
if (type == SOCK_BOOLEAN && !attribute_name) {
name_row->label("", ICON_NONE);
prop_row = &split->row(true);
}
else {
prop_row = &layout->row(true);
}
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
if (type == SOCK_BOOLEAN) {
uiLayoutSetPropSep(prop_row, false);
uiLayoutSetAlignment(prop_row, UI_LAYOUT_ALIGN_EXPAND);
}
if (attribute_name) {
name_row->label(socket.name ? IFACE_(socket.name) : "", ICON_NONE);
prop_row = &split->row(true);
add_attribute_search_button(ctx, prop_row, rna_path_attribute_name, socket, false);
layout->label("", ICON_BLANK1);
}
else {
const char *name = socket.name ? IFACE_(socket.name) : "";
prop_row->prop(ctx.md_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
uiItemDecoratorR(layout, ctx.md_ptr, rna_path.c_str(), -1);
}
PointerRNA props;
uiItemFullO(prop_row,
"object.geometry_nodes_input_attribute_toggle",
"",
ICON_SPREADSHEET,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&props);
RNA_string_set(&props, "modifier_name", ctx.nmd.modifier.name);
RNA_string_set(&props, "input_name", socket.identifier);
}
static void layer_name_search_update_fn(
const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
{
const SocketSearchData &data = *static_cast<SocketSearchData *>(arg);
const NodesModifierData *nmd = get_modifier_data(*CTX_data_main(C), *CTX_wm_manager(C), data);
if (nmd == nullptr) {
return;
}
if (nmd->node_group == nullptr) {
return;
}
geo_log::GeoTreeLog *tree_log = get_root_tree_log(*nmd);
if (tree_log == nullptr) {
return;
}
tree_log->ensure_layer_names();
nmd->node_group->ensure_topology_cache();
Vector<const bNodeSocket *> sockets_to_check;
for (const bNode *node : nmd->node_group->group_input_nodes()) {
for (const bNodeSocket *socket : node->output_sockets()) {
if (socket->type == SOCK_GEOMETRY) {
sockets_to_check.append(socket);
}
}
}
Set<StringRef> names;
Vector<const std::string *> layer_names;
for (const bNodeSocket *socket : sockets_to_check) {
const geo_log::ValueLog *value_log = tree_log->find_socket_value_log(*socket);
if (value_log == nullptr) {
continue;
}
if (const auto *geo_log = dynamic_cast<const geo_log::GeometryInfoLog *>(value_log)) {
if (const std::optional<geo_log::GeometryInfoLog::GreasePencilInfo> &grease_pencil_info =
geo_log->grease_pencil_info)
{
for (const std::string &name : grease_pencil_info->layer_names) {
if (names.add(name)) {
layer_names.append(&name);
}
}
}
}
}
BLI_assert(items);
ui::grease_pencil_layer_search_add_items(str, layer_names.as_span(), *items, is_first);
}
static void layer_name_search_exec_fn(bContext *C, void *data_v, void *item_v)
{
const SocketSearchData &data = *static_cast<SocketSearchData *>(data_v);
const std::string *item = static_cast<std::string *>(item_v);
if (item == nullptr) {
return;
}
const NodesModifierData *nmd = get_modifier_data(*CTX_data_main(C), *CTX_wm_manager(C), data);
if (nmd == nullptr) {
return;
}
IDProperty &name_property = *IDP_GetPropertyFromGroup(nmd->settings.properties,
data.socket_identifier);
IDP_AssignString(&name_property, item->c_str());
ED_undo_push(C, "Assign Layer Name");
}
static void add_layer_name_search_button(DrawGroupInputsContext &ctx,
uiLayout *layout,
const StringRefNull socket_id_esc,
const bNodeTreeInterfaceSocket &socket)
{
const std::string rna_path = fmt::format("[\"{}\"]", socket_id_esc);
if (!ctx.nmd.runtime->eval_log) {
layout->prop(ctx.md_ptr, rna_path, UI_ITEM_NONE, "", ICON_NONE);
return;
}
uiLayoutSetPropDecorate(layout, false);
uiLayout *split = &layout->split(0.4f, false);
uiLayout *name_row = &split->row(false);
uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT);
name_row->label(socket.name ? IFACE_(socket.name) : "", ICON_NONE);
uiLayout *prop_row = &split->row(true);
uiBlock *block = uiLayoutGetBlock(prop_row);
uiBut *but = uiDefIconTextButR(block,
UI_BTYPE_SEARCH_MENU,
0,
ICON_OUTLINER_DATA_GP_LAYER,
"",
0,
0,
10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */
UI_UNIT_Y,
ctx.md_ptr,
rna_path,
0,
0.0f,
0.0f,
StringRef(socket.description));
UI_but_placeholder_set(but, "Layer");
layout->label("", ICON_BLANK1);
const Object *object = ed::object::context_object(&ctx.C);
BLI_assert(object != nullptr);
if (object == nullptr) {
return;
}
SocketSearchData *data = MEM_callocN<SocketSearchData>(__func__);
data->object_session_uid = object->id.session_uid;
STRNCPY(data->modifier_name, ctx.nmd.modifier.name);
STRNCPY(data->socket_identifier, socket.identifier);
UI_but_func_search_set_results_are_suggestions(but, true);
UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP);
UI_but_func_search_set(but,
nullptr,
layer_name_search_update_fn,
static_cast<void *>(data),
true,
nullptr,
layer_name_search_exec_fn,
nullptr);
}
/* Drawing the properties manually with #uiLayout::prop instead of #uiDefAutoButsRNA allows using
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
* the node socket identifier for the property names, since they are unique, but also having
* the correct label displayed in the UI. */
static void draw_property_for_socket(DrawGroupInputsContext &ctx,
uiLayout *layout,
const bNodeTreeInterfaceSocket &socket)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const StringRefNull identifier = socket.identifier;
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
/* The property should be created in #MOD_nodes_update_interface with the correct type. */
IDProperty *property = ctx.properties.lookup_key_default_as(identifier, nullptr);
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
if (property == nullptr || !nodes::id_property_type_matches_socket(socket, *property)) {
return;
}
const std::string socket_id_esc = BLI_str_escape(identifier.c_str());
const std::string rna_path = fmt::format("[\"{}\"]", socket_id_esc);
Nodes: automatically gray out input values that don't affect the output This patch automatically grays out input values which can't affect the output currently. It works with inputs of group nodes, geometry nodes modifiers and node tools. To achieve this, it analyses the node tree and partially evaluates it to figure out which group inputs are currently not linked to an output or are disabled by e.g. some switch node. Original proposal: https://devtalk.blender.org/t/dynamic-socket-visibility/31874 Related info in blog post: https://code.blender.org/2023/11/geometry-nodes-workshop-november-2023/#dynamic-socket-visibility Follow up task for designing a UI that allows hiding sockets: #132706 Limitations: * The inferencing does not update correctly when a socket starts being animated/driven. I haven't found a good way to invalidate the cache in a good way reliably yet. It's only a very short term problem though. It fixes itself after the next modification of the node tree and is only noticeable when animating some specific sockets such as the switch node condition. * Whether a socket is grayed out is not exposed in the Python API yet. That will be done separately. * Only a partial evaluation is done to determine if an input affects an output. There should be no cases where a socket is found to be unused when it can actually affect the output. However, there can be cases where a socket is inferenced to be used even if it is not due to some complex condition. Depending on the exact circumstances, this can either be improved or the condition in the node tree should be simplified. Pull Request: https://projects.blender.org/blender/blender/pulls/132219
2025-01-21 12:53:24 +01:00
const int input_index = ctx.nmd.node_group->interface_input_index(socket);
uiLayout *row = &layout->row(true);
uiLayoutSetPropDecorate(row, true);
Nodes: automatically gray out input values that don't affect the output This patch automatically grays out input values which can't affect the output currently. It works with inputs of group nodes, geometry nodes modifiers and node tools. To achieve this, it analyses the node tree and partially evaluates it to figure out which group inputs are currently not linked to an output or are disabled by e.g. some switch node. Original proposal: https://devtalk.blender.org/t/dynamic-socket-visibility/31874 Related info in blog post: https://code.blender.org/2023/11/geometry-nodes-workshop-november-2023/#dynamic-socket-visibility Follow up task for designing a UI that allows hiding sockets: #132706 Limitations: * The inferencing does not update correctly when a socket starts being animated/driven. I haven't found a good way to invalidate the cache in a good way reliably yet. It's only a very short term problem though. It fixes itself after the next modification of the node tree and is only noticeable when animating some specific sockets such as the switch node condition. * Whether a socket is grayed out is not exposed in the Python API yet. That will be done separately. * Only a partial evaluation is done to determine if an input affects an output. There should be no cases where a socket is found to be unused when it can actually affect the output. However, there can be cases where a socket is inferenced to be used even if it is not due to some complex condition. Depending on the exact circumstances, this can either be improved or the condition in the node tree should be simplified. Pull Request: https://projects.blender.org/blender/blender/pulls/132219
2025-01-21 12:53:24 +01:00
uiLayoutSetActive(row, ctx.input_usages[input_index]);
/* Use #uiItemPointerR to draw pointer properties because #uiLayout::prop would not have enough
* information about what type of ID to select for editing the values. This is because
* pointer IDProperties contain no information about their type. */
const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
const char *name = socket.name ? IFACE_(socket.name) : "";
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
switch (type) {
case SOCK_OBJECT: {
uiItemPointerR(row, ctx.md_ptr, rna_path, ctx.bmain_ptr, "objects", name, ICON_OBJECT_DATA);
break;
}
case SOCK_COLLECTION: {
uiItemPointerR(
row, ctx.md_ptr, rna_path, ctx.bmain_ptr, "collections", name, ICON_OUTLINER_COLLECTION);
break;
}
case SOCK_MATERIAL: {
uiItemPointerR(row, ctx.md_ptr, rna_path, ctx.bmain_ptr, "materials", name, ICON_MATERIAL);
break;
}
case SOCK_TEXTURE: {
uiItemPointerR(row, ctx.md_ptr, rna_path, ctx.bmain_ptr, "textures", name, ICON_TEXTURE);
break;
}
case SOCK_IMAGE: {
uiTemplateID(row,
&ctx.C,
ctx.md_ptr,
rna_path,
"image.new",
"image.open",
nullptr,
UI_TEMPLATE_ID_FILTER_ALL,
false,
name);
break;
}
case SOCK_MENU: {
if (socket.flag & NODE_INTERFACE_SOCKET_MENU_EXPANDED) {
/* Use a single space when the name is empty to work around a bug with expanded enums. Also
* see #ui_item_enum_expand_exec. */
row->prop(ctx.md_ptr,
rna_path,
UI_ITEM_R_EXPAND,
StringRef(name).is_empty() ? " " : name,
ICON_NONE);
}
else {
row->prop(ctx.md_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
}
break;
}
case SOCK_BOOLEAN: {
if (is_layer_selection_field(socket)) {
add_layer_name_search_button(ctx, row, socket_id_esc, socket);
/* Adds a spacing at the end of the row. */
row->label("", ICON_BLANK1);
break;
}
ATTR_FALLTHROUGH;
}
default: {
if (nodes::input_has_attribute_toggle(*ctx.nmd.node_group, input_index)) {
add_attribute_search_or_value_buttons(ctx, row, socket_id_esc, rna_path, socket);
}
else {
row->prop(ctx.md_ptr, rna_path, UI_ITEM_NONE, name, ICON_NONE);
}
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
if (!nodes::input_has_attribute_toggle(*ctx.nmd.node_group, input_index)) {
row->label("", ICON_BLANK1);
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
static void draw_property_for_output_socket(DrawGroupInputsContext &ctx,
uiLayout *layout,
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const bNodeTreeInterfaceSocket &socket)
{
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const StringRefNull identifier = socket.identifier;
char socket_id_esc[MAX_NAME * 2];
BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc));
const std::string rna_path_attribute_name = fmt::format(
"[\"{}{}\"]", socket_id_esc, nodes::input_attribute_name_suffix);
uiLayout *split = &layout->split(0.4f, false);
uiLayout *name_row = &split->row(false);
uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT);
name_row->label(socket.name ? socket.name : "", ICON_NONE);
uiLayout *row = &split->row(true);
add_attribute_search_button(ctx, row, rna_path_attribute_name, socket, true);
}
static NodesModifierPanel *find_panel_by_id(NodesModifierData &nmd, const int id)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
for (const int i : IndexRange(nmd.panels_num)) {
if (nmd.panels[i].id == id) {
return &nmd.panels[i];
}
}
return nullptr;
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
2024-12-23 12:21:00 +01:00
static bool interface_panel_has_socket(const bNodeTreeInterfacePanel &interface_panel)
{
for (const bNodeTreeInterfaceItem *item : interface_panel.items()) {
if (item->item_type == NODE_INTERFACE_SOCKET) {
const bNodeTreeInterfaceSocket &socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(
item);
if ((socket.flag &
(NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER | NODE_INTERFACE_SOCKET_OUTPUT)) == 0)
{
return true;
}
}
if (item->item_type == NODE_INTERFACE_PANEL) {
2024-12-23 12:21:00 +01:00
if (interface_panel_has_socket(*reinterpret_cast<const bNodeTreeInterfacePanel *>(item))) {
return true;
}
}
}
return false;
}
Nodes: automatically gray out input values that don't affect the output This patch automatically grays out input values which can't affect the output currently. It works with inputs of group nodes, geometry nodes modifiers and node tools. To achieve this, it analyses the node tree and partially evaluates it to figure out which group inputs are currently not linked to an output or are disabled by e.g. some switch node. Original proposal: https://devtalk.blender.org/t/dynamic-socket-visibility/31874 Related info in blog post: https://code.blender.org/2023/11/geometry-nodes-workshop-november-2023/#dynamic-socket-visibility Follow up task for designing a UI that allows hiding sockets: #132706 Limitations: * The inferencing does not update correctly when a socket starts being animated/driven. I haven't found a good way to invalidate the cache in a good way reliably yet. It's only a very short term problem though. It fixes itself after the next modification of the node tree and is only noticeable when animating some specific sockets such as the switch node condition. * Whether a socket is grayed out is not exposed in the Python API yet. That will be done separately. * Only a partial evaluation is done to determine if an input affects an output. There should be no cases where a socket is found to be unused when it can actually affect the output. However, there can be cases where a socket is inferenced to be used even if it is not due to some complex condition. Depending on the exact circumstances, this can either be improved or the condition in the node tree should be simplified. Pull Request: https://projects.blender.org/blender/blender/pulls/132219
2025-01-21 12:53:24 +01:00
static bool interface_panel_affects_output(DrawGroupInputsContext &ctx,
const bNodeTreeInterfacePanel &panel)
{
for (const bNodeTreeInterfaceItem *item : panel.items()) {
if (item->item_type == NODE_INTERFACE_SOCKET) {
const auto &socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(item);
if (socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER) {
continue;
}
if (!(socket.flag & NODE_INTERFACE_SOCKET_INPUT)) {
continue;
}
Nodes: automatically gray out input values that don't affect the output This patch automatically grays out input values which can't affect the output currently. It works with inputs of group nodes, geometry nodes modifiers and node tools. To achieve this, it analyses the node tree and partially evaluates it to figure out which group inputs are currently not linked to an output or are disabled by e.g. some switch node. Original proposal: https://devtalk.blender.org/t/dynamic-socket-visibility/31874 Related info in blog post: https://code.blender.org/2023/11/geometry-nodes-workshop-november-2023/#dynamic-socket-visibility Follow up task for designing a UI that allows hiding sockets: #132706 Limitations: * The inferencing does not update correctly when a socket starts being animated/driven. I haven't found a good way to invalidate the cache in a good way reliably yet. It's only a very short term problem though. It fixes itself after the next modification of the node tree and is only noticeable when animating some specific sockets such as the switch node condition. * Whether a socket is grayed out is not exposed in the Python API yet. That will be done separately. * Only a partial evaluation is done to determine if an input affects an output. There should be no cases where a socket is found to be unused when it can actually affect the output. However, there can be cases where a socket is inferenced to be used even if it is not due to some complex condition. Depending on the exact circumstances, this can either be improved or the condition in the node tree should be simplified. Pull Request: https://projects.blender.org/blender/blender/pulls/132219
2025-01-21 12:53:24 +01:00
const int input_index = ctx.nmd.node_group->interface_input_index(socket);
if (ctx.input_usages[input_index]) {
return true;
}
}
else if (item->item_type == NODE_INTERFACE_PANEL) {
const auto &sub_interface_panel = *reinterpret_cast<const bNodeTreeInterfacePanel *>(item);
if (interface_panel_affects_output(ctx, sub_interface_panel)) {
return true;
}
}
}
return false;
}
static void draw_interface_panel_content(DrawGroupInputsContext &ctx,
uiLayout *layout,
const bNodeTreeInterfacePanel &interface_panel,
const bool skip_first = false)
{
for (const bNodeTreeInterfaceItem *item : interface_panel.items().drop_front(skip_first ? 1 : 0))
{
if (item->item_type == NODE_INTERFACE_PANEL) {
const auto &sub_interface_panel = *reinterpret_cast<const bNodeTreeInterfacePanel *>(item);
2024-12-23 12:21:00 +01:00
if (!interface_panel_has_socket(sub_interface_panel)) {
continue;
}
NodesModifierPanel *panel = find_panel_by_id(ctx.nmd, sub_interface_panel.identifier);
PointerRNA panel_ptr = RNA_pointer_create_discrete(
ctx.md_ptr->owner_id, &RNA_NodesModifierPanel, panel);
PanelLayout panel_layout;
bool skip_first = false;
/* Check if the panel should have a toggle in the header. */
const bNodeTreeInterfaceSocket *toggle_socket = sub_interface_panel.header_toggle_socket();
if (toggle_socket && !(toggle_socket->flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) {
const StringRefNull identifier = toggle_socket->identifier;
IDProperty *property = ctx.properties.lookup_key_default_as(identifier, nullptr);
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
if (property == nullptr ||
!nodes::id_property_type_matches_socket(*toggle_socket, *property))
{
continue;
}
char socket_id_esc[MAX_NAME * 2];
BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc));
char rna_path[sizeof(socket_id_esc) + 4];
SNPRINTF(rna_path, "[\"%s\"]", socket_id_esc);
panel_layout = layout->panel_prop_with_bool_header(
&ctx.C, &panel_ptr, "is_open", ctx.md_ptr, rna_path, IFACE_(sub_interface_panel.name));
skip_first = true;
}
else {
panel_layout = layout->panel_prop(&ctx.C, &panel_ptr, "is_open");
panel_layout.header->label(IFACE_(sub_interface_panel.name), ICON_NONE);
}
Nodes: automatically gray out input values that don't affect the output This patch automatically grays out input values which can't affect the output currently. It works with inputs of group nodes, geometry nodes modifiers and node tools. To achieve this, it analyses the node tree and partially evaluates it to figure out which group inputs are currently not linked to an output or are disabled by e.g. some switch node. Original proposal: https://devtalk.blender.org/t/dynamic-socket-visibility/31874 Related info in blog post: https://code.blender.org/2023/11/geometry-nodes-workshop-november-2023/#dynamic-socket-visibility Follow up task for designing a UI that allows hiding sockets: #132706 Limitations: * The inferencing does not update correctly when a socket starts being animated/driven. I haven't found a good way to invalidate the cache in a good way reliably yet. It's only a very short term problem though. It fixes itself after the next modification of the node tree and is only noticeable when animating some specific sockets such as the switch node condition. * Whether a socket is grayed out is not exposed in the Python API yet. That will be done separately. * Only a partial evaluation is done to determine if an input affects an output. There should be no cases where a socket is found to be unused when it can actually affect the output. However, there can be cases where a socket is inferenced to be used even if it is not due to some complex condition. Depending on the exact circumstances, this can either be improved or the condition in the node tree should be simplified. Pull Request: https://projects.blender.org/blender/blender/pulls/132219
2025-01-21 12:53:24 +01:00
if (!interface_panel_affects_output(ctx, sub_interface_panel)) {
uiLayoutSetActive(panel_layout.header, false);
}
uiLayoutSetTooltipFunc(
panel_layout.header,
[](bContext * /*C*/, void *panel_arg, const StringRef /*tip*/) -> std::string {
const auto *panel = static_cast<bNodeTreeInterfacePanel *>(panel_arg);
return StringRef(panel->description);
},
const_cast<bNodeTreeInterfacePanel *>(&sub_interface_panel),
nullptr,
nullptr);
if (panel_layout.body) {
draw_interface_panel_content(ctx, panel_layout.body, sub_interface_panel, skip_first);
}
}
else {
const auto &interface_socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(item);
if (interface_socket.flag & NODE_INTERFACE_SOCKET_INPUT) {
if (!(interface_socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) {
draw_property_for_socket(ctx, layout, interface_socket);
}
}
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
}
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
static bool has_output_attribute(const NodesModifierData &nmd)
{
if (!nmd.node_group) {
return false;
}
for (const bNodeTreeInterfaceSocket *interface_socket : nmd.node_group->interface_outputs()) {
const bke::bNodeSocketType *typeinfo = interface_socket->socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
if (nodes::socket_type_has_attribute_toggle(type)) {
return true;
}
}
return false;
}
static void draw_output_attributes_panel(DrawGroupInputsContext &ctx, uiLayout *layout)
{
if (ctx.nmd.node_group != nullptr && ctx.nmd.settings.properties != nullptr) {
for (const bNodeTreeInterfaceSocket *socket : ctx.nmd.node_group->interface_outputs()) {
const bke::bNodeSocketType *typeinfo = socket->socket_typeinfo();
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
SOCK_CUSTOM;
if (nodes::socket_type_has_attribute_toggle(type)) {
draw_property_for_output_socket(ctx, layout, *socket);
}
}
}
}
static void draw_bake_panel(uiLayout *layout, PointerRNA *modifier_ptr)
{
uiLayout *col = &layout->column(false);
uiLayoutSetPropSep(col, true);
uiLayoutSetPropDecorate(col, false);
col->prop(modifier_ptr, "bake_target", UI_ITEM_NONE, std::nullopt, ICON_NONE);
col->prop(modifier_ptr, "bake_directory", UI_ITEM_NONE, IFACE_("Bake Path"), ICON_NONE);
}
static void draw_named_attributes_panel(uiLayout *layout, NodesModifierData &nmd)
{
if (G.is_rendering) {
/* Avoid accessing this data while baking in a separate thread. */
return;
}
geo_log::GeoTreeLog *tree_log = get_root_tree_log(nmd);
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
if (tree_log == nullptr) {
return;
}
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
tree_log->ensure_used_named_attributes();
const Map<StringRefNull, geo_log::NamedAttributeUsage> &usage_by_attribute =
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
2022-09-13 08:44:26 +02:00
tree_log->used_named_attributes;
if (usage_by_attribute.is_empty()) {
layout->label(RPT_("No named attributes used"), ICON_INFO);
return;
}
struct NameWithUsage {
StringRefNull name;
geo_log::NamedAttributeUsage usage;
};
Vector<NameWithUsage> sorted_used_attribute;
for (auto &&item : usage_by_attribute.items()) {
sorted_used_attribute.append({item.key, item.value});
}
std::sort(sorted_used_attribute.begin(),
sorted_used_attribute.end(),
[](const NameWithUsage &a, const NameWithUsage &b) {
return BLI_strcasecmp_natural(a.name.c_str(), b.name.c_str()) < 0;
});
for (const NameWithUsage &attribute : sorted_used_attribute) {
const StringRef attribute_name = attribute.name;
const geo_log::NamedAttributeUsage usage = attribute.usage;
/* #uiLayoutRowWithHeading doesn't seem to work in this case. */
uiLayout *split = &layout->split(0.4f, false);
std::stringstream ss;
Vector<std::string> usages;
if ((usage & geo_log::NamedAttributeUsage::Read) != geo_log::NamedAttributeUsage::None) {
usages.append(IFACE_("Read"));
}
if ((usage & geo_log::NamedAttributeUsage::Write) != geo_log::NamedAttributeUsage::None) {
usages.append(IFACE_("Write"));
}
if ((usage & geo_log::NamedAttributeUsage::Remove) != geo_log::NamedAttributeUsage::None) {
usages.append(CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove"));
}
for (const int i : usages.index_range()) {
ss << usages[i];
if (i < usages.size() - 1) {
ss << ", ";
}
}
uiLayout *row = &split->row(false);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
uiLayoutSetActive(row, false);
row->label(ss.str(), ICON_NONE);
row = &split->row(false);
row->label(attribute_name, ICON_NONE);
}
}
static void draw_manage_panel(const bContext *C,
uiLayout *layout,
PointerRNA *modifier_ptr,
NodesModifierData &nmd)
{
if (uiLayout *panel_layout = layout->panel_prop(
C, modifier_ptr, "open_bake_panel", IFACE_("Bake")))
{
draw_bake_panel(panel_layout, modifier_ptr);
}
if (uiLayout *panel_layout = layout->panel_prop(
C, modifier_ptr, "open_named_attributes_panel", IFACE_("Named Attributes")))
{
draw_named_attributes_panel(panel_layout, nmd);
}
}
static void draw_warnings(const bContext *C,
const NodesModifierData &nmd,
uiLayout *layout,
PointerRNA *md_ptr)
{
if (G.is_rendering) {
/* Avoid accessing this data while baking in a separate thread. */
return;
}
using namespace geo_log;
GeoTreeLog *tree_log = get_root_tree_log(nmd);
if (!tree_log) {
return;
}
tree_log->ensure_node_warnings(*CTX_data_main(C));
const int warnings_num = tree_log->all_warnings.size();
if (warnings_num == 0) {
return;
}
PanelLayout panel = layout->panel_prop(C, md_ptr, "open_warnings_panel");
panel.header->label(fmt::format(fmt::runtime(IFACE_("Warnings ({})")), warnings_num).c_str(),
ICON_NONE);
if (!panel.body) {
return;
}
Vector<const NodeWarning *> warnings(tree_log->all_warnings.size());
for (const int i : warnings.index_range()) {
warnings[i] = &tree_log->all_warnings[i];
}
std::sort(warnings.begin(), warnings.end(), [](const NodeWarning *a, const NodeWarning *b) {
const int severity_a = node_warning_type_severity(a->type);
const int severity_b = node_warning_type_severity(b->type);
if (severity_a > severity_b) {
return true;
}
if (severity_a < severity_b) {
return false;
}
return BLI_strcasecmp_natural(a->message.c_str(), b->message.c_str()) < 0;
});
uiLayout *col = &panel.body->column(false);
for (const NodeWarning *warning : warnings) {
const int icon = node_warning_type_icon(warning->type);
col->label(warning->message, icon);
}
}
static void panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data);
uiLayoutSetPropSep(layout, true);
/* Decorators are added manually for supported properties because the
* attribute/value toggle requires a manually built layout anyway. */
uiLayoutSetPropDecorate(layout, false);
if (!(nmd->flag & NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR)) {
const char *newop = (nmd->node_group == nullptr) ? "node.new_geometry_node_group_assign" :
"object.geometry_node_tree_copy_assign";
uiTemplateID(layout, C, ptr, "node_group", newop, nullptr, nullptr);
}
Main *bmain = CTX_data_main(C);
PointerRNA bmain_ptr = RNA_main_pointer_create(bmain);
DrawGroupInputsContext ctx{
*C, *nmd, nodes::build_properties_vector_set(nmd->settings.properties), ptr, &bmain_ptr};
if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) {
nmd->node_group->ensure_interface_cache();
Nodes: automatically gray out input values that don't affect the output This patch automatically grays out input values which can't affect the output currently. It works with inputs of group nodes, geometry nodes modifiers and node tools. To achieve this, it analyses the node tree and partially evaluates it to figure out which group inputs are currently not linked to an output or are disabled by e.g. some switch node. Original proposal: https://devtalk.blender.org/t/dynamic-socket-visibility/31874 Related info in blog post: https://code.blender.org/2023/11/geometry-nodes-workshop-november-2023/#dynamic-socket-visibility Follow up task for designing a UI that allows hiding sockets: #132706 Limitations: * The inferencing does not update correctly when a socket starts being animated/driven. I haven't found a good way to invalidate the cache in a good way reliably yet. It's only a very short term problem though. It fixes itself after the next modification of the node tree and is only noticeable when animating some specific sockets such as the switch node condition. * Whether a socket is grayed out is not exposed in the Python API yet. That will be done separately. * Only a partial evaluation is done to determine if an input affects an output. There should be no cases where a socket is found to be unused when it can actually affect the output. However, there can be cases where a socket is inferenced to be used even if it is not due to some complex condition. Depending on the exact circumstances, this can either be improved or the condition in the node tree should be simplified. Pull Request: https://projects.blender.org/blender/blender/pulls/132219
2025-01-21 12:53:24 +01:00
ctx.input_usages.reinitialize(nmd->node_group->interface_inputs().size());
nodes::socket_usage_inference::infer_group_interface_inputs_usage(
*nmd->node_group, ctx.properties, ctx.input_usages);
draw_interface_panel_content(ctx, layout, nmd->node_group->tree_interface.root_panel);
}
modifier_error_message_draw(layout, ptr);
draw_warnings(C, *nmd, layout, ptr);
if (has_output_attribute(*nmd)) {
if (uiLayout *panel_layout = layout->panel_prop(
C, ptr, "open_output_attributes_panel", IFACE_("Output Attributes")))
{
draw_output_attributes_panel(ctx, panel_layout);
}
}
if (uiLayout *panel_layout = layout->panel_prop(C, ptr, "open_manage_panel", IFACE_("Manage"))) {
draw_manage_panel(C, panel_layout, ptr, *nmd);
}
}
static void panel_register(ARegionType *region_type)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
using namespace blender;
modifier_panel_register(region_type, eModifierType_Nodes, panel_draw);
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
BLO_write_struct(writer, NodesModifierData, nmd);
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
BLO_write_string(writer, nmd->bake_directory);
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
if (nmd->settings.properties != nullptr) {
Map<IDProperty *, IDPropertyUIDataBool *> boolean_props;
if (!BLO_write_is_undo(writer)) {
/* Boolean properties are added automatically for boolean node group inputs. Integer
* properties are automatically converted to boolean sockets where applicable as well.
* However, boolean properties will crash old versions of Blender, so convert them to integer
* properties for writing. The actual value is stored in the same variable for both types */
LISTBASE_FOREACH (IDProperty *, prop, &nmd->settings.properties->data.group) {
if (prop->type == IDP_BOOLEAN) {
boolean_props.add_new(prop, reinterpret_cast<IDPropertyUIDataBool *>(prop->ui_data));
prop->type = IDP_INT;
prop->ui_data = nullptr;
}
}
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
/* Note that the property settings are based on the socket type info
* and don't necessarily need to be written, but we can't just free them. */
IDP_BlendWrite(writer, nmd->settings.properties);
BLO_write_struct_array(writer, NodesModifierBake, nmd->bakes_num, nmd->bakes);
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
BLO_write_string(writer, bake.directory);
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
BLO_write_struct_array(
writer, NodesModifierDataBlock, bake.data_blocks_num, bake.data_blocks);
for (const NodesModifierDataBlock &item : Span(bake.data_blocks, bake.data_blocks_num)) {
BLO_write_string(writer, item.id_name);
BLO_write_string(writer, item.lib_name);
}
if (bake.packed) {
BLO_write_struct(writer, NodesModifierPackedBake, bake.packed);
BLO_write_struct_array(
writer, NodesModifierBakeFile, bake.packed->meta_files_num, bake.packed->meta_files);
BLO_write_struct_array(
writer, NodesModifierBakeFile, bake.packed->blob_files_num, bake.packed->blob_files);
const auto write_bake_file = [&](const NodesModifierBakeFile &bake_file) {
BLO_write_string(writer, bake_file.name);
if (bake_file.packed_file) {
BKE_packedfile_blend_write(writer, bake_file.packed_file);
}
};
for (const NodesModifierBakeFile &meta_file :
Span{bake.packed->meta_files, bake.packed->meta_files_num})
{
write_bake_file(meta_file);
}
for (const NodesModifierBakeFile &blob_file :
Span{bake.packed->blob_files, bake.packed->blob_files_num})
{
write_bake_file(blob_file);
}
}
}
BLO_write_struct_array(writer, NodesModifierPanel, nmd->panels_num, nmd->panels);
if (!BLO_write_is_undo(writer)) {
LISTBASE_FOREACH (IDProperty *, prop, &nmd->settings.properties->data.group) {
if (prop->type == IDP_INT) {
if (IDPropertyUIDataBool **ui_data = boolean_props.lookup_ptr(prop)) {
prop->type = IDP_BOOLEAN;
if (ui_data) {
prop->ui_data = reinterpret_cast<IDPropertyUIData *>(*ui_data);
}
}
}
}
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
BLO_read_string(reader, &nmd->bake_directory);
if (nmd->node_group == nullptr) {
nmd->settings.properties = nullptr;
}
else {
BLO_read_struct(reader, IDProperty, &nmd->settings.properties);
IDP_BlendDataRead(reader, &nmd->settings.properties);
}
BLO_read_struct_array(reader, NodesModifierBake, nmd->bakes_num, &nmd->bakes);
if (nmd->bakes_num > 0 && nmd->bakes == nullptr) {
/* This case generally shouldn't be allowed to happen. However, there is a bug report with a
* corrupted .blend file (#123974) that triggers this case. Unfortunately, it's not clear how
* that could have happened. For now, handle this case more gracefully in release builds, while
* still crashing in debug builds. */
nmd->bakes_num = 0;
BLI_assert_unreachable();
}
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
BLO_read_string(reader, &bake.directory);
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
BLO_read_struct_array(reader, NodesModifierDataBlock, bake.data_blocks_num, &bake.data_blocks);
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
for (NodesModifierDataBlock &data_block : MutableSpan(bake.data_blocks, bake.data_blocks_num))
{
BLO_read_string(reader, &data_block.id_name);
BLO_read_string(reader, &data_block.lib_name);
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
}
BLO_read_struct(reader, NodesModifierPackedBake, &bake.packed);
if (bake.packed) {
BLO_read_struct_array(
reader, NodesModifierBakeFile, bake.packed->meta_files_num, &bake.packed->meta_files);
BLO_read_struct_array(
reader, NodesModifierBakeFile, bake.packed->blob_files_num, &bake.packed->blob_files);
const auto read_bake_file = [&](NodesModifierBakeFile &bake_file) {
BLO_read_string(reader, &bake_file.name);
if (bake_file.packed_file) {
BKE_packedfile_blend_read(reader, &bake_file.packed_file, "");
}
};
for (NodesModifierBakeFile &meta_file :
MutableSpan{bake.packed->meta_files, bake.packed->meta_files_num})
{
read_bake_file(meta_file);
}
for (NodesModifierBakeFile &blob_file :
MutableSpan{bake.packed->blob_files, bake.packed->blob_files_num})
{
read_bake_file(blob_file);
}
}
}
BLO_read_struct_array(reader, NodesModifierPanel, nmd->panels_num, &nmd->panels);
nmd->runtime = MEM_new<NodesModifierRuntime>(__func__);
nmd->runtime->cache = std::make_shared<bake::ModifierCache>();
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
NodesModifierData *tnmd = reinterpret_cast<NodesModifierData *>(target);
BKE_modifier_copydata_generic(md, target, flag);
if (nmd->bakes) {
tnmd->bakes = static_cast<NodesModifierBake *>(MEM_dupallocN(nmd->bakes));
for (const int i : IndexRange(nmd->bakes_num)) {
NodesModifierBake &bake = tnmd->bakes[i];
if (bake.directory) {
bake.directory = BLI_strdup(bake.directory);
}
Geometry Nodes: support baking data block references With this patch, materials are kept intact in simulation zones and bake nodes without any additional user action. This implements the design proposed in #108410 to support referencing data-blocks (only materials for now) in the baked data. The task also describes why this is not a trivial issue. A previous attempt was implemented in #109703 but it didn't work well-enough. The solution is to have an explicit `name (+ library name) -> data-block` mapping that is stored in the modifier for each bake node and simulation zone. The `library name` is necessary for it to be unique within a .blend file. Note that this refers to the name of the `Library` data-block and not a file path. The baked data only contains the names of the used data-blocks. When the baked data is loaded, the correct material data-block is looked up from the mapping. ### Automatic Mapping Generation The most tricky aspect of this approach is to make it feel mostly automatic. From the user point-of-view, it should just work. Therefore, we don't want the user to have to create the mapping manually in the majority of cases. Creating the mapping automatically is difficult because the data-blocks that should become part of the mapping are only known during depsgraph evaluation. So we somehow have to gather the missing data blocks during evaluation and then write the new mappings back to the original data. While writing back to original data is something we do in some cases already, the situation here is different, because we are actually creating new relations between data-blocks. This also means that we'll have to do user-counting. Since user counts in data-blocks are *not* atomic, we can't do that from multiple threads at the same time. Also, under some circumstances, it may be necessary to trigger depsgraph evaluation again after the write-back because it actually affects the result. To solve this, a small new API is added in `DEG_depsgraph_writeback_sync.hh`. It allows gathering tasks which write back to original data in a synchronous way which may also require a reevaluation. ### Accessing the Mapping A new `BakeDataBlockMap` is passed to geometry nodes evaluation by the modifier. This map allows getting the `ID` pointer that should be used for a specific data-block name that is stored in baked data. It's also used to gather all the missing data mappings during evaluation. ### Weak ID References The baked/cached geometries may have references to other data-blocks (currently only materials, but in the future also e.g. instanced objects/collections). However, the pointers of these data-blocks are not stable over time. That is especially true when storing/loading the data from disk, but also just when playing back the animation. Therefore, the used data-blocks have to referenced in a different way at run-time. This is solved by adding `std::unique_ptr<bake::BakeMaterialsList>` to the run-time data of various geometry data-blocks. If the data-block is cached over a longer period of time (such that material pointers can't be used directly), it stores the material name (+ library name) used by each material slot. When the geometry is used again, the material pointers are restored using these weak name references and the `BakeDataBlockMap`. ### Manual Mapping Management There is a new `Data-Blocks` panel in the bake settings in the node editor sidebar that allows inspecting and modifying the data-blocks that are used when baking. The user can change what data-block a specific name is mapped to. Pull Request: https://projects.blender.org/blender/blender/pulls/117043
2024-02-01 09:21:55 +01:00
if (bake.data_blocks) {
bake.data_blocks = static_cast<NodesModifierDataBlock *>(MEM_dupallocN(bake.data_blocks));
for (const int i : IndexRange(bake.data_blocks_num)) {
NodesModifierDataBlock &data_block = bake.data_blocks[i];
if (data_block.id_name) {
data_block.id_name = BLI_strdup(data_block.id_name);
}
if (data_block.lib_name) {
data_block.lib_name = BLI_strdup(data_block.lib_name);
}
}
}
if (bake.packed) {
bake.packed = static_cast<NodesModifierPackedBake *>(MEM_dupallocN(bake.packed));
const auto copy_bake_files_inplace = [](NodesModifierBakeFile **bake_files,
const int bake_files_num) {
if (!*bake_files) {
return;
}
*bake_files = static_cast<NodesModifierBakeFile *>(MEM_dupallocN(*bake_files));
for (NodesModifierBakeFile &bake_file : MutableSpan{*bake_files, bake_files_num}) {
bake_file.name = BLI_strdup_null(bake_file.name);
if (bake_file.packed_file) {
bake_file.packed_file = BKE_packedfile_duplicate(bake_file.packed_file);
}
}
};
copy_bake_files_inplace(&bake.packed->meta_files, bake.packed->meta_files_num);
copy_bake_files_inplace(&bake.packed->blob_files, bake.packed->blob_files_num);
}
}
}
if (nmd->panels) {
tnmd->panels = static_cast<NodesModifierPanel *>(MEM_dupallocN(nmd->panels));
}
tnmd->runtime = MEM_new<NodesModifierRuntime>(__func__);
if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) {
/* Share the simulation cache between the original and evaluated modifier. */
tnmd->runtime->cache = nmd->runtime->cache;
/* Keep bake path in the evaluated modifier. */
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
tnmd->bake_directory = nmd->bake_directory ? BLI_strdup(nmd->bake_directory) : nullptr;
}
else {
tnmd->runtime->cache = std::make_shared<bake::ModifierCache>();
/* Clear the bake path when duplicating. */
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
tnmd->bake_directory = nullptr;
}
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
if (nmd->settings.properties != nullptr) {
tnmd->settings.properties = IDP_CopyProperty_ex(nmd->settings.properties, flag);
}
}
void nodes_modifier_packed_bake_free(NodesModifierPackedBake *packed_bake)
{
const auto free_packed_files = [](NodesModifierBakeFile *files, const int files_num) {
for (NodesModifierBakeFile &file : MutableSpan{files, files_num}) {
MEM_SAFE_FREE(file.name);
if (file.packed_file) {
BKE_packedfile_free(file.packed_file);
}
}
MEM_SAFE_FREE(files);
};
free_packed_files(packed_bake->meta_files, packed_bake->meta_files_num);
free_packed_files(packed_bake->blob_files, packed_bake->blob_files_num);
MEM_SAFE_FREE(packed_bake);
}
void nodes_modifier_bake_destruct(NodesModifierBake *bake, const bool do_id_user)
{
MEM_SAFE_FREE(bake->directory);
for (NodesModifierDataBlock &data_block : MutableSpan(bake->data_blocks, bake->data_blocks_num))
{
nodes_modifier_data_block_destruct(&data_block, do_id_user);
}
MEM_SAFE_FREE(bake->data_blocks);
if (bake->packed) {
nodes_modifier_packed_bake_free(bake->packed);
}
}
static void free_data(ModifierData *md)
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->settings.properties != nullptr) {
IDP_FreeProperty_ex(nmd->settings.properties, false);
nmd->settings.properties = nullptr;
}
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
nodes_modifier_bake_destruct(&bake, false);
}
MEM_SAFE_FREE(nmd->bakes);
MEM_SAFE_FREE(nmd->panels);
Geometry Nodes: new Bake node This adds a new `Bake` node which allows saving and loading intermediate geometries. Typical use cases we want address with this currently are: * Bake some data for use with a render engine. * Bake parts of the node tree explicitly for better performance. For now, the format that is written to disk is not considered to be an import/export format. It's not guaranteed that data written with one Blender version can be read by another Blender version. For that it's better to use proper interchange formats. Better support for those will be added eventually as well. We also plan an `Import Bake` node that allows reading the blender-specific baked data independent of the Bake node and at different frames. The baking works very similar to the baking in the simulation zone (UI and implementation wise). Major differences are: * The Bake node has a `Bake Still` and `Bake Animation` mode. * The Bake node doesn't do automatic caching. Implementation details: * Refactored how we create the Python operators for moving socket items so that it also makes sense for non-zones. * The `ModifierCache` stores an independent map of `SimulationNodeCache` and `BakeNodeCache`, but both share a common data structure for the actually baked data. * For baking, the `Bake` node is added as a side-effect-node in the modifier. This will make sure that the node is baked even if it's currently not connected to the output. * Had to add a new `DEG_id_tag_update_for_side_effect_request` function that is used during baking. It's necessary because I want to evaluate the object again even though none of its inputs changed. The reevaluation is necessary to create the baked data. Using `DEG_id_tag_update` technically works as well, but has the problem that it also uses the `DEG_UPDATE_SOURCE_USER_EDIT` flag which (rightly) invalidates simulation caches which shouldn't happen here. * Slightly refactored the timeline drawing so that it can also show the baked ranges of Bake nodes. It does not show anything for baked nodes with a in Still mode though. * The bake operator is refactored to bake a list of `NodeBakeRequest` which makes the code easier to follow compared to the previous nested `ObjectBakeData > ModifierBakeData > NodeBakeData` data structure. * The bake operators are disabled when the .blend file is not yet saved. This is technically only necessary when the bake path depends on the .blend file path but seems ok to force the user anyway (otherwise the bake path may be lost as well if it's set explicitly). * The same operators are used to bake and delete single bakes in `Bake` nodes and `Simulation Zones`. On top of that, there are separate operators of baking and deleting all simulation bakes (those ignore bake nodes). * The `Bake` node remembers which inputs have been fields and thus may be baked as attributes. For that it uses an `Is Attribute` flag on the socket item. This is needed because the baked data may still contain attribute data, even if the inputs to the bake node are disconnected. * Similar to simulation zones, the behavior of `Bake` nodes is passed into the geometry nodes evaluation from the outside (from the modifier only currently). This is done by providing the new `GeoNodesBakeParams` in `GeoNodesCallData` when executing geometry nodes. Next Steps (mostly because they also involve simulations): * Visualize nodes that have not been evaluated in the last evaluation. * Fix issue with seemingly loosing baked data after undo. * Improve error handling when baked data is not found. * Show bake node in link drag search. * Higher level tools for managing bakes. Pull Request: https://projects.blender.org/blender/blender/pulls/115466
2023-12-18 13:01:06 +01:00
MEM_SAFE_FREE(nmd->bake_directory);
MEM_delete(nmd->runtime);
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
}
static void required_data_mask(ModifierData * /*md*/, CustomData_MeshMasks *r_cddata_masks)
{
/* We don't know what the node tree will need. If there are vertex groups, it is likely that the
* node tree wants to access them. */
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
r_cddata_masks->vmask |= CD_MASK_PROP_ALL;
}
} // namespace blender
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
ModifierTypeInfo modifierType_Nodes = {
/*idname*/ "GeometryNodes",
/*name*/ N_("GeometryNodes"),
/*struct_name*/ "NodesModifierData",
/*struct_size*/ sizeof(NodesModifierData),
/*srna*/ &RNA_NodesModifier,
/*type*/ ModifierTypeType::Constructive,
/*flags*/
(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs |
eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode |
eModifierTypeFlag_SupportsMapping | eModifierTypeFlag_AcceptsGreasePencil),
/*icon*/ ICON_GEOMETRY_NODES,
/*copy_data*/ blender::copy_data,
/*deform_verts*/ nullptr,
/*deform_matrices*/ nullptr,
/*deform_verts_EM*/ nullptr,
/*deform_matrices_EM*/ nullptr,
/*modify_mesh*/ blender::modify_mesh,
/*modify_geometry_set*/ blender::modify_geometry_set,
/*init_data*/ blender::init_data,
/*required_data_mask*/ blender::required_data_mask,
/*free_data*/ blender::free_data,
/*is_disabled*/ blender::is_disabled,
/*update_depsgraph*/ blender::update_depsgraph,
/*depends_on_time*/ blender::depends_on_time,
/*depends_on_normals*/ nullptr,
/*foreach_ID_link*/ blender::foreach_ID_link,
/*foreach_tex_link*/ blender::foreach_tex_link,
/*free_runtime_data*/ nullptr,
/*panel_register*/ blender::panel_register,
/*blend_write*/ blender::blend_write,
/*blend_read*/ blender::blend_read,
/*foreach_cache*/ nullptr,
Geometry Nodes: initial scattering and geometry nodes This is the initial merge from the geometry-nodes branch. Nodes: * Attribute Math * Boolean * Edge Split * Float Compare * Object Info * Point Distribute * Point Instance * Random Attribute * Random Float * Subdivision Surface * Transform * Triangulate It includes the initial evaluation of geometry node groups in the Geometry Nodes modifier. Notes on the Generic attribute access API The API adds an indirection for attribute access. That has the following benefits: * Most code does not have to care about how an attribute is stored internally. This is mainly necessary, because we have to deal with "legacy" attributes such as vertex weights and attributes that are embedded into other structs such as vertex positions. * When reading from an attribute, we generally don't care what domain the attribute is stored on. So we want to abstract away the interpolation that that adapts attributes from one domain to another domain (this is not actually implemented yet). Other possible improvements for later iterations include: * Actually implement interpolation between domains. * Don't use inheritance for the different attribute types. A single class for read access and one for write access might be enough, because we know all the ways in which attributes are stored internally. We don't want more different internal structures in the future. On the contrary, ideally we can consolidate the different storage formats in the future to reduce the need for this indirection. * Remove the need for heap allocations when creating attribute accessors. It includes commits from: * Dalai Felinto * Hans Goudey * Jacques Lucke * Léo Depoix
2020-12-02 13:25:25 +01:00
};