From f02ee8a6af67abc4962d497a4b939f521d2f36be Mon Sep 17 00:00:00 2001
From: huihantao <losanc@users.noreply.github.com>
Date: Mon, 12 Sep 2022 17:27:45 +0200
Subject: [PATCH] Weekly updates (#5)

* restructure to support additional file formats

* update for dim3 template

* rename simloader to bseq

* minor fix

* add notes

* use blender driver system to control frames

* small update with frame driver

* disbale the function when not in object mode

* small fix

* rewrite edit

* add global settings for refresh
---
 bseq/__init__.py   | 28 +++++-----------------------
 bseq/callback.py   |  5 ++++-
 bseq/globals.py    | 41 +++++++++++++++++++++++++++++++++++++++++
 bseq/importer.py   |  8 +++++---
 bseq/messenger.py  |  3 ++-
 bseq/operators.py  | 25 +++++++++----------------
 bseq/panels.py     | 11 +++++++++--
 bseq/properties.py | 10 ++++++++++
 bseq/utils.py      | 14 +++++++++++++-
 9 files changed, 98 insertions(+), 47 deletions(-)
 create mode 100644 bseq/globals.py

diff --git a/bseq/__init__.py b/bseq/__init__.py
index abcd742..edaf58c 100644
--- a/bseq/__init__.py
+++ b/bseq/__init__.py
@@ -1,36 +1,18 @@
-from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins,BSEQ_OT_set_as_split_norm,BSEQ_OT_remove_split_norm,BSEQ_OT_disable_selected,BSEQ_OT_enable_selected,BSEQ_OT_refresh_seq
-from .properties import BSEQ_scene_property, BSEQ_obj_property,BSEQ_mesh_property
+from bseq.utils import refresh_obj
+from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq
+from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property
 from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_Import, BSEQ_Templates, BSEQ_UL_Att_List, draw_template
 from .messenger import subscribe_to_selected, unsubscribe_to_selected
 import bpy
 from bpy.app.handlers import persistent
 from .importer import update_obj
-from datetime import datetime
-
-
-def print_information(scene):
-    if not bpy.context.scene.BSEQ.print:
-        return
-    now = datetime.now()
-    path = bpy.context.scene.render.filepath
-    path = bpy.path.abspath(path)
-    filepath = path + '/bseq_' + now.strftime("%Y-%m-%d_%H-%M")
-    with open(filepath, 'w') as file:
-        file.write("Render Time: {}\n".format(now.strftime("%Y-%m-%d_%H-%M")))
-        file.write("bseq Objects in the scene:\n\n")
-        for obj in bpy.data.objects:
-            bseq_prop = obj.BSEQ
-            if bseq_prop.init:
-                file.write("Object name: {}\n".format(obj.name))
-                file.write("Is it being animated: {}\n".format(bseq_prop.enabled))
-                file.write("Filepath: {}\n".format(bseq_prop.pattern))
-                file.write("Is it relative path: {}\n".format(bseq_prop.use_relative))
-                file.write("\n\n")
+from .globals import *    
 
 
 @persistent
 def BSEQ_initialize(scene):
     if update_obj not in bpy.app.handlers.frame_change_post:
+        bpy.app.handlers.frame_change_post.append(auto_refresh)
         bpy.app.handlers.frame_change_post.append(update_obj)
     subscribe_to_selected()
     if print_information not in bpy.app.handlers.render_init:
diff --git a/bseq/callback.py b/bseq/callback.py
index 02b41a9..a2c6ece 100644
--- a/bseq/callback.py
+++ b/bseq/callback.py
@@ -48,4 +48,7 @@ def update_selected_obj_num(self, context):
 
 
 def poll_material(self, material):
-    return not material.is_grease_pencil
\ No newline at end of file
+    return not material.is_grease_pencil
+
+def poll_edit_obj(self, object):
+    return object.BSEQ.init
\ No newline at end of file
diff --git a/bseq/globals.py b/bseq/globals.py
new file mode 100644
index 0000000..5577a71
--- /dev/null
+++ b/bseq/globals.py
@@ -0,0 +1,41 @@
+# here are the implementations of global settings
+
+import bpy
+from datetime import datetime
+import os
+from .utils import refresh_obj
+
+def print_information(scene):
+    if not bpy.context.scene.BSEQ.print:
+        return
+    now = datetime.now()
+    path = bpy.context.scene.render.filepath
+    path = bpy.path.abspath(path)
+    if not os.path.isdir(path):
+        # by default, path is '/tmp', and it does not exist on windows system
+        return
+    filepath = path + '/bseq_' + now.strftime("%Y-%m-%d_%H-%M")
+    with open(filepath, 'w') as file:
+        file.write("Render Time: {}\n".format(now.strftime("%Y-%m-%d_%H-%M")))
+        file.write("bseq Objects in the scene:\n\n")
+        for obj in bpy.data.objects:
+            bseq_prop = obj.BSEQ
+            if bseq_prop.init:
+                file.write("Object name: {}\n".format(obj.name))
+                file.write("Is it being animated: {}\n".format(bseq_prop.enabled))
+                file.write("Filepath: {}\n".format(bseq_prop.pattern))
+                file.write("Is it relative path: {}\n".format(bseq_prop.use_relative))
+                file.write("\n\n")
+
+
+def auto_refresh(scene, depsgraph=None):
+    if not bpy.context.scene.BSEQ.auto_refresh:
+        return
+    for obj in bpy.data.objects:
+        if obj.BSEQ.init == False:
+            continue
+        if obj.BSEQ.enabled == False:
+            continue
+        if obj.mode != "OBJECT":
+            continue
+        refresh_obj(obj)
\ No newline at end of file
diff --git a/bseq/importer.py b/bseq/importer.py
index 972b14b..4c896d0 100644
--- a/bseq/importer.py
+++ b/bseq/importer.py
@@ -164,6 +164,8 @@ def create_obj(fileseq, use_relaitve, transform_matrix=Matrix([[1, 0, 0, 0], [0,
     object.BSEQ.init = True
     object.BSEQ.enabled = enabled
     object.matrix_world = transform_matrix
+    driver = object.driver_add("BSEQ.frame")
+    driver.driver.expression = 'frame'
     if enabled:
         update_mesh(meshio_mesh, object.data)
     bpy.context.collection.objects.link(object)
@@ -172,16 +174,16 @@ def create_obj(fileseq, use_relaitve, transform_matrix=Matrix([[1, 0, 0, 0], [0,
 
 
 def update_obj(scene, depsgraph=None):
-    # TODO if bpy in edit mode, then return
-
-    current_frame = bpy.context.scene.frame_current
 
     for obj in bpy.data.objects:
         if obj.BSEQ.init == False:
             continue
         if obj.BSEQ.enabled == False:
             continue
+        if obj.mode != "OBJECT":
+            continue
 
+        current_frame = obj.BSEQ.frame
         meshio_mesh = None
         pattern = obj.BSEQ.pattern
         if obj.BSEQ.use_relative:
diff --git a/bseq/messenger.py b/bseq/messenger.py
index e497e74..d2956f5 100644
--- a/bseq/messenger.py
+++ b/bseq/messenger.py
@@ -10,7 +10,8 @@ def selected_callback():
         bpy.context.scene.BSEQ.selected_obj_deselectall_flag = False
         bpy.context.scene.BSEQ.selected_obj_num = idx
         bpy.context.scene.BSEQ.selected_obj_deselectall_flag = True
-
+    if bpy.context.active_object.BSEQ.init:
+        bpy.context.scene.BSEQ.edit_obj = bpy.context.active_object
 
 def subscribe_to_selected():
     import bseq
diff --git a/bseq/operators.py b/bseq/operators.py
index 78106e4..05592d1 100644
--- a/bseq/operators.py
+++ b/bseq/operators.py
@@ -2,7 +2,7 @@
 import fileseq
 from .messenger import *
 import traceback
-from .utils import show_message_box
+from .utils import refresh_obj, show_message_box
 from .importer import create_obj
 import numpy as np
 
@@ -79,10 +79,12 @@ def execute(self, context):
             return {"CANCELLED"}
 
         sim_loader = context.scene.BSEQ
+
+        # logic here
         #  it seems quite simple task, no need to create a function(for now)
-        if sim_loader.selected_obj_num >= len(bpy.data.objects):
+        obj = sim_loader.edit_obj
+        if not obj:
             return {"CANCELLED"}
-        obj = bpy.data.objects[sim_loader.selected_obj_num]
         if importer_prop.relative:
             obj.BSEQ.pattern = bpy.path.relpath(str(fs))
         else:
@@ -110,7 +112,7 @@ def execute(self, context):
         gn = obj.modifiers.new("BSEQ_GeometryNodse", "NODES")
         # change starting from blender 3.2
         # https://developer.blender.org/rB08b4b657b64f
-        if bpy.app.version >= (3,2,0):
+        if bpy.app.version >= (3, 2, 0):
             bpy.ops.node.new_geometry_node_group_assign()
         gn.node_group.nodes.new('GeometryNodeMeshToPoints')
         set_material = gn.node_group.nodes.new('GeometryNodeSetMaterial')
@@ -145,7 +147,7 @@ def execute(self, context):
         gn = obj.modifiers.new("BSEQ_GeometryNodse", "NODES")
         # change starting from blender 3.2
         # https://developer.blender.org/rB08b4b657b64f
-        if bpy.app.version >= (3,2,0):
+        if bpy.app.version >= (3, 2, 0):
             bpy.ops.node.new_geometry_node_group_assign()
         bpy.ops.object.modifier_move_to_index(modifier=gn.name, index=0)
         return {"FINISHED"}
@@ -170,7 +172,7 @@ def execute(self, context):
         gn = obj.modifiers.new("BSEQ_GeometryNodse", "NODES")
         # change starting from blender 3.2
         # https://developer.blender.org/rB08b4b657b64f
-        if bpy.app.version >= (3,2,0):
+        if bpy.app.version >= (3, 2, 0):
             bpy.ops.node.new_geometry_node_group_assign()
         nodes = gn.node_group.nodes
         links = gn.node_group.links
@@ -268,15 +270,6 @@ class BSEQ_OT_refresh_seq(bpy.types.Operator):
     def execute(self, context):
         scene = context.scene
         obj = bpy.data.objects[scene.BSEQ.selected_obj_num]
-
-        fs = obj.BSEQ.pattern
-        if obj.BSEQ.use_relative:
-            fs = bpy.path.abspath(fs)
-        fs = fileseq.findSequenceOnDisk(fs)
-        fs = fileseq.findSequenceOnDisk(fs.dirname() + fs.basename() + "@" + fs.extension())
-        fs = str(fs)
-        if obj.BSEQ.use_relative:
-            fs = bpy.path.relpath(fs)
-        obj.BSEQ.pattern = fs
+        refresh_obj(obj)
 
         return {"FINISHED"}
diff --git a/bseq/panels.py b/bseq/panels.py
index b009476..7467fe7 100644
--- a/bseq/panels.py
+++ b/bseq/panels.py
@@ -26,8 +26,10 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn
             row.prop(item, "name", text='Name ', emboss=False)
             if item.BSEQ.enabled:
                 row.prop(item.BSEQ, "enabled", text = "ENABLED", icon="PLAY")
+                row.prop(item.BSEQ, "frame", text = "Current Frame:")
             else:
                 row.prop(item.BSEQ, "enabled", text = "DISABLED", icon="PAUSE")
+                row.label(text = "Animation Stopped")
         else:
             # actually, I guess this line of code won't be executed?
             layout.label(text="", translate=False, icon_value=icon)
@@ -71,8 +73,6 @@ def draw(self, context):
         row = layout.row()
         row.operator("bseq.enableselected", text="Enable Selected")
         row.operator("bseq.disableselected", text="Disable Selected")
-        row = layout.row()
-        row.operator("sequence.edit", text="Edit Path")
         row.operator("bseq.refresh", text="Refresh")
 
 
@@ -193,6 +193,11 @@ def draw(self, context):
         col2.prop(importer_prop, "relative", text="")
 
         layout.operator("sequence.load")
+        split = layout.split()
+        col1 = split.column()
+        col2 = split.column()
+        col1.prop_search(importer_prop, 'edit_obj', bpy.data, 'objects', text="")
+        col2.operator("sequence.edit")
 
         layout.label(text="Global Settings")
         box = layout.box()
@@ -203,6 +208,8 @@ def draw(self, context):
 
         col1.label(text="Print Sequence Information on Render")
         col2.prop(importer_prop, "print", text="")
+        col1.label(text="Auto refresh all the sequence every frame")
+        col2.prop(importer_prop, "auto_refresh", text="")
 
 
 class BSEQ_Templates(bpy.types.Menu):
diff --git a/bseq/properties.py b/bseq/properties.py
index 5cfcb9f..73e1ded 100644
--- a/bseq/properties.py
+++ b/bseq/properties.py
@@ -32,10 +32,19 @@ class BSEQ_scene_property(bpy.types.PropertyGroup):
         poll=poll_material,
     )
 
+    edit_obj: bpy.props.PointerProperty(
+        type=bpy.types.Object,
+        poll=poll_edit_obj,
+    )
+
     print: bpy.props.BoolProperty(name='print',
                                   description="whether or not to print additional information when rendering",
                                   default=True)
 
+    auto_refresh: bpy.props.BoolProperty(name='auto refresh',
+                                         description="whether or not to auto refresh all the sequence every frame",
+                                         default=False)
+
 
 class BSEQ_obj_property(bpy.types.PropertyGroup):
     init: bpy.props.BoolProperty(default=False)
@@ -45,6 +54,7 @@ class BSEQ_obj_property(bpy.types.PropertyGroup):
     script_name: bpy.props.StringProperty()
     use_relative: bpy.props.BoolProperty(default=False)
     pattern: bpy.props.StringProperty()
+    frame: bpy.props.IntProperty()
 
 
 # set this property for mesh, not object (maybe change later?)
diff --git a/bseq/utils.py b/bseq/utils.py
index 7896675..775ee6b 100644
--- a/bseq/utils.py
+++ b/bseq/utils.py
@@ -1,5 +1,5 @@
 import bpy
-
+import fileseq
 
 def show_message_box(message="", title="Message Box", icon="INFO"):
     '''
@@ -24,3 +24,15 @@ def stop_animation():
         #  if playing animation, then stop it, otherwise it will keep showing message box
         bpy.ops.screen.animation_cancel()
 
+
+
+def refresh_obj(obj):
+    fs = obj.BSEQ.pattern
+    if obj.BSEQ.use_relative:
+        fs = bpy.path.abspath(fs)
+    fs = fileseq.findSequenceOnDisk(fs)
+    fs = fileseq.findSequenceOnDisk(fs.dirname() + fs.basename() + "@" + fs.extension())
+    fs = str(fs)
+    if obj.BSEQ.use_relative:
+        fs = bpy.path.relpath(fs)
+    obj.BSEQ.pattern = fs
\ No newline at end of file
