This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/scripts/startup/bl_ui/__init__.py
Jacques Lucke 0de54b84c6 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: blender/blender#104924
2023-05-03 13:18:59 +02:00

272 lines
8.2 KiB
Python

# SPDX-License-Identifier: GPL-2.0-or-later
# note, properties_animviz is a helper module only.
# support reloading sub-modules
if "bpy" in locals():
from importlib import reload
_modules_loaded[:] = [reload(val) for val in _modules_loaded]
del reload
_modules = [
"node_add_menu",
"node_add_menu_geometry",
"properties_animviz",
"properties_constraint",
"properties_data_armature",
"properties_data_bone",
"properties_data_camera",
"properties_data_curve",
"properties_data_curves",
"properties_data_empty",
"properties_data_gpencil",
"properties_data_light",
"properties_data_lattice",
"properties_data_mesh",
"properties_data_metaball",
"properties_data_modifier",
"properties_data_pointcloud",
"properties_data_shaderfx",
"properties_data_lightprobe",
"properties_data_speaker",
"properties_data_volume",
"properties_mask_common",
"properties_material",
"properties_material_gpencil",
"properties_object",
"properties_paint_common",
"properties_grease_pencil_common",
"properties_particle",
"properties_physics_cloth",
"properties_physics_common",
"properties_physics_dynamicpaint",
"properties_physics_field",
"properties_physics_geometry_nodes",
"properties_physics_rigidbody",
"properties_physics_rigidbody_constraint",
"properties_physics_fluid",
"properties_physics_softbody",
"properties_render",
"properties_output",
"properties_view_layer",
"properties_scene",
"properties_texture",
"properties_world",
"properties_collection",
"generic_ui_list",
# Generic Space Modules
#
# Depends on DNA_WORKSPACE_TOOL (C define).
"space_toolsystem_common",
"space_toolsystem_toolbar",
"space_clip",
"space_console",
"space_dopesheet",
"space_filebrowser",
"space_graph",
"space_image",
"space_info",
"space_nla",
"space_node",
"space_outliner",
"space_properties",
"space_sequencer",
"space_spreadsheet",
"space_statusbar",
"space_text",
"space_time",
"space_topbar",
"space_userpref",
"space_view3d",
"space_view3d_toolbar",
# XXX, keep last so panels show after all other tool options.
"properties_workspace",
]
import bpy
if bpy.app.build_options.freestyle:
_modules.append("properties_freestyle")
__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = [_namespace[name] for name in _modules]
del _namespace
def register():
from bpy.utils import register_class
for mod in _modules_loaded:
for cls in mod.classes:
register_class(cls)
space_filebrowser.register_props()
from bpy.props import (
EnumProperty,
StringProperty,
)
from bpy.types import (
WindowManager,
)
# space_userprefs.py
def addon_filter_items(_self, _context):
import addon_utils
items = [
('All', "All", "All Add-ons"),
('User', "User", "All Add-ons Installed by User"),
]
items_unique = set()
for mod in addon_utils.modules(refresh=False):
info = addon_utils.module_bl_info(mod)
items_unique.add(info["category"])
items.extend([(cat, cat, "") for cat in sorted(items_unique)])
return items
WindowManager.addon_search = StringProperty(
name="Filter",
description="Filter by add-on name, author & category",
options={'TEXTEDIT_UPDATE'},
)
WindowManager.addon_filter = EnumProperty(
items=addon_filter_items,
name="Category",
description="Filter add-ons by category",
)
# These items are static but depend on the version cycle.
items = [
('OFFICIAL', "Official", "Officially supported"),
('COMMUNITY', "Community", "Maintained by community developers"),
]
if bpy.app.version_cycle == "alpha":
items.append(('TESTING', "Testing", "Newly contributed scripts (excluded from release builds)"))
WindowManager.addon_support = EnumProperty(
items=items,
name="Support",
description="Display support level",
default={'OFFICIAL', 'COMMUNITY'},
options={'ENUM_FLAG'},
)
del items
# done...
def unregister():
from bpy.utils import unregister_class
for mod in reversed(_modules_loaded):
for cls in reversed(mod.classes):
if cls.is_registered:
unregister_class(cls)
# Define a default UIList, when a list does not need any custom drawing...
# Keep in sync with its #defined name in UI_interface.h
class UI_UL_list(bpy.types.UIList):
# These are common filtering or ordering operations (same as the default C ones!).
@staticmethod
def filter_items_by_name(pattern, bitflag, items, propname="name", flags=None, reverse=False):
"""
Set FILTER_ITEM for items which name matches filter_name one (case-insensitive).
pattern is the filtering pattern.
propname is the name of the string property to use for filtering.
flags must be a list of integers the same length as items, or None!
return a list of flags (based on given flags if not None),
or an empty list if no flags were given and no filtering has been done.
"""
import fnmatch
if not pattern or not items: # Empty pattern or list = no filtering!
return flags or []
if flags is None:
flags = [0] * len(items)
# Implicitly add heading/trailing wildcards.
pattern = "*" + pattern + "*"
for i, item in enumerate(items):
name = getattr(item, propname, None)
# This is similar to a logical xor
if bool(name and fnmatch.fnmatch(name, pattern)) is not bool(reverse):
flags[i] |= bitflag
return flags
@staticmethod
def sort_items_helper(sort_data, key, reverse=False):
"""
Common sorting utility. Returns a neworder list mapping org_idx -> new_idx.
sort_data must be an (unordered) list of tuples [(org_idx, ...), (org_idx, ...), ...].
key must be the same kind of callable you would use for sorted() builtin function.
reverse will reverse the sorting!
"""
sort_data.sort(key=key, reverse=reverse)
neworder = [None] * len(sort_data)
for newidx, (orgidx, *_) in enumerate(sort_data):
neworder[orgidx] = newidx
return neworder
@classmethod
def sort_items_by_name(cls, items, propname="name"):
"""
Re-order items using their names (case-insensitive).
propname is the name of the string property to use for sorting.
return a list mapping org_idx -> new_idx,
or an empty list if no sorting has been done.
"""
_sort = [(idx, getattr(it, propname, "")) for idx, it in enumerate(items)]
return cls.sort_items_helper(_sort, lambda e: e[1].lower())
bpy.utils.register_class(UI_UL_list)
class UI_MT_list_item_context_menu(bpy.types.Menu):
"""
UI List item context menu definition. Scripts can append/prepend this to
add own operators to the context menu. They must check context though, so
their items only draw in a valid context and for the correct UI list.
"""
bl_label = "List Item"
bl_idname = "UI_MT_list_item_context_menu"
def draw(self, context):
# Dummy function. This type is just for scripts to append their own
# context menu items.
pass
bpy.utils.register_class(UI_MT_list_item_context_menu)
class UI_MT_button_context_menu(bpy.types.Menu):
"""
UI button context menu definition. Scripts can append/prepend this to
add own operators to the context menu. They must check context though, so
their items only draw in a valid context and for the correct buttons.
"""
bl_label = "List Item"
bl_idname = "UI_MT_button_context_menu"
def draw(self, context):
# Draw menu entries created with the legacy `WM_MT_button_context` class.
# This is deprecated, and support will be removed in a future release.
if hasattr(bpy.types, "WM_MT_button_context"):
self.layout.menu_contents("WM_MT_button_context")
bpy.utils.register_class(UI_MT_button_context_menu)