Skip to content

Commit

Permalink
v0.3.3 - Fixed Keying & Onion issues
Browse files Browse the repository at this point in the history
- The addon will now assert that the active object should be of mesh
  type. If not, the appropriate operators will be inactive

- Fixed issue that artists were not able to override keyed value in an
  existing keyframe

- Fixed #9 that the `Initialize Frame Handler` had to be manually
  invoked by artists periodically to avoid issues. This has been fixed
  by calling the frame handler every 180 seconds, and also after a file
  is loaded

- The panel will now display a warning if there are no active objects
  and also when the active object is not of MESH type

- The active object's validity is now checked properly for all operators
  that depends on it.
  • Loading branch information
aldrinmathew committed Feb 19, 2022
1 parent 3e91384 commit 25a23ba
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 56 deletions.
16 changes: 14 additions & 2 deletions stopmagic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from typing import List, Tuple
import bpy
import threading
import re
import os.path

Expand All @@ -28,7 +29,7 @@
bl_info = {
"name": "Stopmagic",
"author": "Aldrin Mathew",
"version": (0, 3, 2),
"version": (0, 3, 3),
"blender": (2, 91, 0),
"location": "Sidebar > Stopmagic",
"warning": "beta",
Expand All @@ -41,6 +42,17 @@
addon_keymaps: List[Tuple[bpy.types.KeyMap, bpy.types.KeyMapItem]] = []


@bpy.app.handlers.persistent
def periodic_handler(_):
functions.frame_handler(None)
threading.Timer(
interval=180,
function=functions.frame_handler,
args=[None],
kwargs=None,
)


def register() -> None:
global addon_keymaps
icons.register()
Expand All @@ -56,7 +68,7 @@ def register() -> None:
operators.keyed_frame_previous.register()
operators.upgrade_addon.register()
operators.contributions.register()
bpy.app.handlers.load_post.append(functions.frame_handler)
bpy.app.handlers.load_post.append(periodic_handler)
bpy.app.handlers.frame_change_post.clear()
bpy.app.handlers.frame_change_post.append(functions.update_stopmagic)
bpy.app.handlers.frame_change_pre.clear()
Expand Down
1 change: 1 addition & 0 deletions stopmagic/functions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
from .get_object_keyframes import *
from .get_object_key_values import *
from .handle_onion_constraints import *
from .is_candidate_object import *
24 changes: 13 additions & 11 deletions stopmagic/functions/handle_onion_skin.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def handle_collections(past: bpy.types.Object, future: bpy.types.Object) -> None
sm_collection.hide_select = True


@bpy.app.handlers.persistent
def handle_onion_skin(dummy):
"""Automatically handles Onion Skin objects and populates them with mesh data"""
if bpy.context.view_layer.objects.active is not None:
Expand Down Expand Up @@ -284,14 +285,15 @@ def clear_onion_data():
bpy.data.materials.remove(pmaterial, do_unlink=True)
if fmaterial is not None:
bpy.data.materials.remove(fmaterial, do_unlink=True)
if collection.get("objects") is not None:
for obj in collection.objects:
mesh = obj.data
obj.name = "removed"
bpy.context.scene.objects.unlink(obj)
bpy.data.meshes.remove(mesh, do_unlink=True)
bpy.data.objects.remove(obj, do_unlink=True)
bpy.data.collections.remove(collection)
bpy.ops.outliner.orphans_purge(
do_local_ids=True, do_linked_ids=False, do_recursive=False
)
if collection is not None:
if collection.get("objects") is not None:
for obj in collection.objects:
mesh = obj.data
obj.name = "removed"
bpy.context.scene.objects.unlink(obj)
bpy.data.meshes.remove(mesh, do_unlink=True)
bpy.data.objects.remove(obj, do_unlink=True)
bpy.data.collections.remove(collection)
bpy.ops.outliner.orphans_purge(
do_local_ids=True, do_linked_ids=False, do_recursive=False
)
55 changes: 29 additions & 26 deletions stopmagic/functions/insert_mesh_keyframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,32 +40,35 @@ def insert_mesh_keyframe(obj: bpy.types.Object | Any) -> None:


def insert_mesh_keyframe_ex(obj: bpy.types.Object) -> bool:
if obj.get("sm_id") is None:
obj["sm_id"] = new_object_id()
#
object_sm_id = obj["sm_id"]
ob_name_full = obj.name_full
mesh_index = get_next_mesh_index(obj)
mesh_name = ob_name_full + "_sm_" + str(mesh_index)
del_mesh = []
new_mesh = bpy.data.meshes.new_from_object(obj)
new_mesh.name = mesh_name
new_mesh["sm_id"] = object_sm_id
new_mesh["sm_datablock"] = mesh_index
obj.data = new_mesh
obj.data.use_fake_user = True
obj["sm_datablock"] = mesh_index
obj.keyframe_insert(
data_path='["sm_datablock"]', frame=bpy.context.scene.frame_current
)
for mesh in del_mesh:
mesh.use_fake_user = False
update_stopmagic(bpy.context.scene)
for mesh in del_mesh:
if mesh.users == 0:
bpy.data.meshes.remove(mesh)
#
return True
try:
if obj.get("sm_id") is None:
obj["sm_id"] = new_object_id()
#
object_sm_id = obj["sm_id"]
ob_name_full = obj.name_full
mesh_index = get_next_mesh_index(obj)
mesh_name = ob_name_full + "_sm_" + str(mesh_index)
del_mesh = []
new_mesh = bpy.data.meshes.new_from_object(obj)
new_mesh.name = mesh_name
new_mesh["sm_id"] = object_sm_id
new_mesh["sm_datablock"] = mesh_index
obj.data = new_mesh
obj.data.use_fake_user = True
obj["sm_datablock"] = mesh_index
obj.keyframe_insert(
data_path='["sm_datablock"]', frame=bpy.context.scene.frame_current
)
for mesh in del_mesh:
mesh.use_fake_user = False
update_stopmagic(bpy.context.scene)
for mesh in del_mesh:
if mesh.users == 0:
bpy.data.meshes.remove(mesh)
#
return True
except:
return False


# Get the appropriate index for the mesh about to be created
Expand Down
1 change: 1 addition & 0 deletions stopmagic/functions/update_stopmagic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import bpy


@bpy.app.handlers.persistent
def update_stopmagic(scene: bpy.types.Scene) -> None:
for object in scene.objects:
if object.get("sm_datablock") is None:
Expand Down
4 changes: 2 additions & 2 deletions stopmagic/operators/add_mesh_keyframe.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import bpy
from ..functions.insert_mesh_keyframe import *
from stopmagic.functions import insert_mesh_keyframe, is_candidate_object


class AddMeshKeyframe(bpy.types.Operator):
Expand All @@ -11,7 +11,7 @@ class AddMeshKeyframe(bpy.types.Operator):

@classmethod
def poll(cls, context):
return context.view_layer.objects.active is not None
return is_candidate_object(context)

def execute(self, context: bpy.types.Context):
if bpy.context.view_layer.objects.active is not None:
Expand Down
11 changes: 6 additions & 5 deletions stopmagic/operators/keyed_frame_next.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import List, Set
import bpy

from stopmagic.functions import get_object_keyframes
from stopmagic.functions import get_object_keyframes, is_candidate_object


class KeyedFrameNext(bpy.types.Operator):
Expand All @@ -14,14 +14,15 @@ class KeyedFrameNext(bpy.types.Operator):

@classmethod
def poll(cls, context: bpy.types.Context) -> bool:
return context.view_layer.objects.active is not None
return is_candidate_object(context)

def execute(self, context: bpy.types.Context) -> Set[int] | Set[str]:
if bpy.context.view_layer.objects.active is not None:
if context.view_layer.objects.active is not None:
keyframes = get_object_keyframes(context.view_layer.objects.active)
if len(keyframes) > 0:
frame = bpy.context.scene.frame_current
keyframes = [k for k in keyframes if k > frame]
keyframes = [
k for k in keyframes if k > bpy.context.scene.frame_current
]
if len(keyframes) > 0:
lowest = keyframes[0]
for num in keyframes:
Expand Down
11 changes: 6 additions & 5 deletions stopmagic/operators/keyed_frame_previous.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import List, Set
import bpy

from stopmagic.functions.get_object_keyframes import get_object_keyframes
from stopmagic.functions import get_object_keyframes, is_candidate_object


class KeyedFramePrevious(bpy.types.Operator):
Expand All @@ -14,14 +14,15 @@ class KeyedFramePrevious(bpy.types.Operator):

@classmethod
def poll(cls, context: bpy.types.Context) -> bool:
return context.view_layer.objects.active is not None
return is_candidate_object(context)

def execute(self, context: bpy.types.Context) -> Set[int] | Set[str]:
if bpy.context.view_layer.objects.active is not None:
if context.view_layer.objects.active is not None:
keyframes = get_object_keyframes(context.view_layer.objects.active)
if len(keyframes) > 0:
frame = bpy.context.scene.frame_current
keyframes = [k for k in keyframes if k < frame]
keyframes = [
k for k in keyframes if k < bpy.context.scene.frame_current
]
if len(keyframes) > 0:
highest = keyframes[0]
for num in keyframes:
Expand Down
4 changes: 2 additions & 2 deletions stopmagic/operators/skip_frame_backward.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations
from typing import Set
import bpy
from stopmagic.functions.insert_mesh_keyframe import insert_mesh_keyframe
from stopmagic.functions import insert_mesh_keyframe, is_candidate_object


class SkipFrameBackward(bpy.types.Operator):
Expand All @@ -13,7 +13,7 @@ class SkipFrameBackward(bpy.types.Operator):

@classmethod
def poll(cls, context: bpy.types.Context) -> bool:
return context.view_layer.objects.active is not None
return is_candidate_object(context)

def execute(self, context: bpy.types.Context) -> Set[int] | Set[str]:
count = 3
Expand Down
4 changes: 2 additions & 2 deletions stopmagic/operators/skip_frame_forward.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations
import bpy
from stopmagic.functions.insert_mesh_keyframe import insert_mesh_keyframe
from stopmagic.functions import insert_mesh_keyframe, is_candidate_object
from typing import Set


Expand All @@ -13,7 +13,7 @@ class SkipFrameForward(bpy.types.Operator):

@classmethod
def poll(cls, context: bpy.types.Context) -> bool:
return context.view_layer.objects.active is not None
return is_candidate_object(context)

def execute(self, context: bpy.types.Context) -> Set[int] | Set[str]:
count = 3
Expand Down
7 changes: 6 additions & 1 deletion stopmagic/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,19 @@ def draw(self, context: bpy.context):
box.label(
text="v"
+ functions.addon_version(StopmagicPanel.bl_info)
+ " >> "
+ " to "
+ addon_remote_version()
)
box.operator(
"stopmagic.upgrade_addon", text="Upgrade Addon", icon="URL"
)
self.layout.separator()
# SECTION :: Keyframe Mesh
if not functions.is_candidate_object(context):
if context.view_layer.objects.active is not None:
self.layout.column().label(text="NO ACTIVE MESH OBJECT!!", icon="ERROR")
else:
self.layout.column().label(text="NO ACTIVE OBJECT!!", icon="ERROR")
column = self.layout.column()
column.scale_y = 1.5
column.operator("object.keyframe_mesh", text="Keyframe Mesh")
Expand Down

0 comments on commit 25a23ba

Please sign in to comment.