Skip to content

Commit

Permalink
Scene handler (#4299)
Browse files Browse the repository at this point in the history
add new scene handler
should update nodes reading data from Blender upon changes in the scene

* remove update live mode

* Remove AnimatableNode mix-in class and create is_animation_dependent property instead. Also add is_scene_dependent property which handled nodes to be updated upon user changes in scene
  • Loading branch information
Durman authored May 6, 2022
1 parent 86957d4 commit 6019f8e
Show file tree
Hide file tree
Showing 61 changed files with 393 additions and 446 deletions.
4 changes: 4 additions & 0 deletions core/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@ class TreeEvent:
NODES_UPDATE = 'nodes_update' # changes in node properties, update animated nodes
FORCE_UPDATE = 'force_update' # rebuild tree and reevaluate every node
FRAME_CHANGE = 'frame_change' # unlike other updates this one should be un-cancellable
SCENE_UPDATE = 'scene_update' # something was changed in the scene

def __init__(self, event_type: str, tree: SverchCustomTree, updated_nodes: Iterable[SvNode] = None, cancel=True):
self.type = event_type
self.tree = tree
self.updated_nodes = updated_nodes
self.cancel = cancel

def __repr__(self):
return f"<TreeEvent type={self.type}>"


class GroupEvent:
GROUP_NODE_UPDATE = 'group_node_update'
Expand Down
9 changes: 8 additions & 1 deletion core/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,19 @@ def update_frame_change_mode():
set_frame_change(mode)


@persistent
def update_trees_scene_change(scene):
for ng in BlTrees().sv_main_trees:
ng.scene_update()


handler_dict = {
'undo_pre': sv_handler_undo_pre,
'undo_post': sv_handler_undo_post,
'load_pre': sv_pre_load,
'load_post': sv_post_load,
'depsgraph_update_pre': sv_main_handler
'depsgraph_update_pre': sv_main_handler,
'depsgraph_update_post': update_trees_scene_change,
}


Expand Down
17 changes: 17 additions & 0 deletions core/main_tree_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ def send(event: TreeEvent):
list(global_updater(event.type))
return

# something changed in scene and it duplicates some tree events which should be ignored
elif event.type == TreeEvent.SCENE_UPDATE:
# Either the scene handler was triggered by changes in the tree or tree is still in progress
if NodesUpdater.has_task():
return # ignore the event
# this event was caused my update system itself and should be ignored
elif 'SKIP_UPDATE' in event.tree:
del event.tree['SKIP_UPDATE']
return
ContextTrees.mark_nodes_outdated(event.tree, event.updated_nodes)

# mark given nodes as outdated
elif event.type == TreeEvent.NODES_UPDATE:
ContextTrees.mark_nodes_outdated(event.tree, event.updated_nodes)
Expand Down Expand Up @@ -239,6 +250,12 @@ def global_updater(event_type: str) -> Generator[Node, None, None]:
bl_tree.update_ui() # this only will update UI of main trees
trees_ui_to_update.discard(bl_tree) # protection from double updating

# this only need to trigger scene changes handler again
bl_tree.nodes[-1].use_custom_color = not bl_tree.nodes[-1].use_custom_color
bl_tree.nodes[-1].use_custom_color = not bl_tree.nodes[-1].use_custom_color
# this indicates that process of the tree is finished and next scene event can be skipped
bl_tree['SKIP_UPDATE'] = True

# this will update all opened trees (in group editors)
# regardless whether the trees was changed or not, including group nodes
for bl_tree in trees_ui_to_update:
Expand Down
29 changes: 17 additions & 12 deletions docs/tree_evaluation_system.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,6 @@ Node property / socket property changes
`Update all` operator (:ref:`layout_manager`)
It is the same as `re-update all nodes` operator but effect all trees in a file.

.. _live_update_operator:

Live update modal operator (:ref:`3d_panel`)
It makes update some nodes, which read information from Blender objects, via timer with update period
about 1/10 second. If the tree did not manage to update while this period next event will be ignored.
In this case there can be a visible lag between user action and tree response.

Frame changes
Update upon frame changes. Extra information `Animation`_.

Expand All @@ -152,19 +145,30 @@ Frame changes
Also you can add shortcut for the operator by pressing :kbd:`RMB` on the button of the operator (active tree panel).
Another way to update is enabling `Live update` mode. In this case only changed put of the tree will be updated.

Scene changes
This trigger reacts on arbitrary changes in scene. Those can be: moving
objects, changing edit / object mode, mesh editing, assign materials etc.


Modes (:ref:`active_tree_panel`)
--------------------------------

Live update
If enabled it means that the tree will be evaluated upon changes in topology or changes in properties of a node
made by user. This property does not effect evaluation upon frame changes or by `re-update all nodes` operator.
If enabled it means that the tree will be evaluated upon changes in its
topology, changes in node properties or scene changes made by user.
This property does not effect evaluation upon frame changes or by
`re-update all nodes` operator.
Enabling the property will call the tree topology changes trigger.

Animate
If enabled the tree will be reevaluated upon frame change. The update can effect not all nodes but only those
which have property `to_animate` enabled.

Scene update
If enabled togather with Live Update the tree will be reevaluated upon
changes in the scene. It will effect only nodes with `interactive`
property enabled.


Animation
=========
Expand All @@ -183,9 +187,10 @@ enabled the node will be update each frame change. This can serve two purposes.

- Firstly this can be used for generating animations. In this case
:doc:`Frame info node <nodes/scene/frame_info_mk2>` will be most useful.
- Secondly updating nodes upon frame change can be used for refreshing nodes which take data from Blender data blocks.
For frame change the left/right arrow buttons can be used. Alternative way is to use
:ref:`Live update operator <live_update_operator>`
- **(Deprecated, the Scene trigger is used instead now)** Secondly updating
nodes upon frame change can be used for refreshing nodes which take data from
Blender data blocks. For frame change the left/right arrow buttons can be
used.


.. warning::
Expand Down
20 changes: 14 additions & 6 deletions docs/user_interface/panels.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Tree item buttons:
Controls all OpenGL viewer of this layout. Viewer, Stethoscope and Viewer Indices
Animate layout
to animate the layout (or not) - may preserve you time.
Scene Update
Update upon changes in the scene
Process layout
Automatically evaluate layout while editing, disable for large or complex layouts (F6)
Draft Mode
Expand Down Expand Up @@ -70,10 +72,19 @@ Animate
If enabled the tree will be reevaluated upon frame change. The update can effect not all nodes but only those
which have property to_animate enabled.

Scene update
If enabled togather with Live Update the tree will be reevaluated upon
changes in the scene. It will effect only nodes with `interactive`
property enabled.


Live update
If enabled it means that the tree will be evaluated upon changes in topology or changes in properties of a node
made by user. This property does not effect evaluation upon frame changes or by **Re-update all nodes** operator.
Enabling the property will call the tree topology changes :ref:`trigger <sv_triggers>`.
If enabled it means that the tree will be evaluated upon changes in its
topology, changes in node properties or scene changes made by user.
This property does not effect evaluation upon frame changes or by
`re-update all nodes` operator.
Enabling the property will call the tree topology changes
:ref:`trigger <sv_triggers>`.

Draft mode
It switches to draft property in :doc:`A number node <../nodes/number/numbers>` and some others.
Expand Down Expand Up @@ -397,9 +408,6 @@ With this panel your layout becomes addon itself. So, you making your life easy.
Since Blender 2.8 this panel has two instances. One instance located on `N` panel in `Tool` category of `3D` editor.
Another located in `Active tool and workspace settings` shelf of `Properties` editor.


**Start live update** - will start update layouts by a timer (several times in second)

**Update all trees** - manual update of all layouts

Node properties list
Expand Down
2 changes: 0 additions & 2 deletions docs/user_interface/shortcuts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ This is a collection of shortcuts useful in the Sverchok node tree, some are Ble

.. image:: https://user-images.githubusercontent.com/10011941/78453090-d4c06180-768f-11ea-8631-422fe63f994e.gif

**Ctrl + Shift + F5** in 3D View window - Enables/Disables "Live Update" mode

**F6** - Enables/Disables the processing of the current node-tree

**F7** - Enables/Disables the Draft mode of the current node-tree
Expand Down
71 changes: 69 additions & 2 deletions node_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ def on_draft_mode_changed(self, context):
update=on_draft_mode_changed,
options=set(),
)
sv_scene_update: BoolProperty(name="Scene update", description="Update upon changes in the scene", options=set(),
default=True)

def update(self):
"""This method is called if collection of nodes or links of the tree was changed"""
Expand All @@ -155,14 +157,34 @@ def update_nodes(self, nodes, cancel=True):
"""This method expects to get list of its nodes which should be updated"""
return TreeHandler.send(TreeEvent(TreeEvent.NODES_UPDATE, self, nodes, cancel))

def scene_update(self):
"""This method should be called by scene changes handler
it ignores events related with S
sverchok trees in other cases it updates nodes which read data from Blender"""
def nodes_to_update():
for node in self.nodes:
try:
if node.is_scene_dependent and node.is_interactive:
yield node
except AttributeError:
pass
if self.sv_scene_update:
TreeHandler.send(TreeEvent(TreeEvent.SCENE_UPDATE, self, nodes_to_update(), cancel=False))

def process_ani(self):
"""
Process the Sverchok node tree if animation layers show true.
For animation callback/handler
"""
def animated_nodes():
for node in self.nodes:
try:
if node.is_animation_dependent and node.is_animatable:
yield node
except AttributeError:
pass
if self.sv_animate:
animated_nodes = (n for n in self.nodes if hasattr(n, 'is_animatable') and n.is_animatable)
TreeHandler.send(TreeEvent(TreeEvent.FRAME_CHANGE, self, animated_nodes))
TreeHandler.send(TreeEvent(TreeEvent.FRAME_CHANGE, self, animated_nodes()))

def update_ui(self):
""" The method get information about node statistic of last update from the handler to show in view space
Expand Down Expand Up @@ -193,6 +215,23 @@ def node_id(self):
self.n_id = str(hash(self) ^ hash(time.monotonic()))
return self.n_id

def update_interactive_mode(self, context):
if self.is_interactive:
self.process_node(context)

is_interactive: BoolProperty(default=True, description="Update node upon changes in the scene",
update=update_interactive_mode, name="Interactive")
is_scene_dependent = False # if True and is_interactive then the node will be updated upon scene changes

def refresh_node(self, context):
if self.refresh:
self.refresh = False
self.process_node(context)

refresh: BoolProperty(name="Update Node", description="Update Node", update=refresh_node)
is_animatable: BoolProperty(name="Animate Node", description="Update Node on frame change", default=True)
is_animation_dependent = False # if True and is_animatable the the node will be updated on frame change

def sv_init(self, context):
"""
This method will be called during node creation
Expand Down Expand Up @@ -417,6 +456,34 @@ class SverchCustomTreeNode(UpdateNodes, NodeUtils):
"""Base class for all nodes"""
_docstring = None # A cache for docstring property

def draw_buttons(self, context, layout):
if self.id_data.bl_idname == SverchCustomTree.bl_idname:
row = layout.row(align=True)
if self.is_animation_dependent:
row.prop(self, 'is_animatable', icon='ANIM', icon_only=True)
if self.is_scene_dependent:
row.prop(self, 'is_interactive', icon='SCENE_DATA', icon_only=True)
if self.is_animation_dependent or self.is_scene_dependent:
row.prop(self, 'refresh', icon='FILE_REFRESH')
self.sv_draw_buttons(context, layout)

def sv_draw_buttons(self, context, layout):
pass

def draw_buttons_ext(self, context, layout):
if self.id_data.bl_idname == SverchCustomTree.bl_idname:
row = layout.row(align=True)
if self.is_animation_dependent:
row.prop(self, 'is_animatable', icon='ANIM')
if self.is_scene_dependent:
row.prop(self, 'is_interactive', icon='SCENE_DATA')
if self.is_animation_dependent or self.is_scene_dependent:
row.prop(self, 'refresh', icon='FILE_REFRESH')
self.sv_draw_buttons_ext(context, layout)

def sv_draw_buttons_ext(self, context, layout):
self.sv_draw_buttons(context, layout)

@classproperty
def docstring(cls):
"""
Expand Down
24 changes: 13 additions & 11 deletions nodes/analyzer/object_insolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,14 @@
# ##### END GPL LICENSE BLOCK #####

import bpy
import mathutils
import numpy as np
from mathutils import Vector
from mathutils.bvhtree import BVHTree

from bpy.props import BoolProperty, IntProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.utils.nodes_mixins.sv_animatable_nodes import SvAnimatableNode
from sverchok.data_structure import (updateNode, match_long_repeat, match_cross)
from sverchok.utils.logging import debug, info, error


class FakeObj(object):

Expand Down Expand Up @@ -56,14 +54,23 @@ def ray_cast(self, a, b):
return [True, tv[0], tv[1], tv[2]]



class SvOBJInsolationNode(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode):
class SvOBJInsolationNode(bpy.types.Node, SverchCustomTreeNode):
''' Insolation by RayCast Object '''
bl_idname = 'SvOBJInsolationNode'
bl_label = 'Object ID Insolation'
bl_icon = 'OUTLINER_OB_EMPTY'
sv_icon = 'SV_INSOLATION'

@property
def is_scene_dependent(self):
return ((not self.inputs['Predator'].is_linked and self.inputs['Predator'].object_ref_pointer)
or (not self.inputs['Victim'].is_linked and self.inputs['Victim'].object_ref_pointer))

@property
def is_animation_dependent(self):
return ((not self.inputs['Predator'].is_linked and self.inputs['Predator'].object_ref_pointer)
or (not self.inputs['Victim'].is_linked and self.inputs['Victim'].object_ref_pointer))

mode: BoolProperty(name='input mode', default=False, update=updateNode)
#mode2 = BoolProperty(name='output mode', default=False, update=updateNode)
sort_critical: IntProperty(name='sort_critical', default=12, min=1,max=24, update=updateNode)
Expand All @@ -81,11 +88,7 @@ def sv_init(self, context):
so('SvStringsSocket', "Hours")
# self.inputs[2].prop[2] = -1 # z down # <--- mayybe?

def draw_buttons(self, context, layout):
self.draw_animatable_buttons(layout, icon_only=True)

def draw_buttons_ext(self, context, layout):
self.draw_animatable_buttons(layout)
def sv_draw_buttons_ext(self, context, layout):
row = layout.row(align=True)
row.prop(self, "mode", text="In Mode")
row.prop(self, "sort_critical",text="Limit")
Expand Down Expand Up @@ -137,7 +140,6 @@ def colset(rec,OutS_):
rec.data.vertex_colors.new(name='SvInsol')
colors = rec.data.vertex_colors['SvInsol'].data
for i, pol in enumerate(rec.data.polygons):
self.debug(pol.loop_indices,OutS[0][i])
for co in pol.loop_indices:
colors[co].color = OutS[0][i]

Expand Down
Loading

0 comments on commit 6019f8e

Please sign in to comment.