Created
April 22, 2023 02:26
-
-
Save nathanielanozie/d8c74c9282d65e3c0145084c35989e4e to your computer and use it in GitHub Desktop.
work in progress arc tracker for Blender 2.79
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bpy | |
import imp | |
bl_info = { | |
"name": "arc tracker (package)", | |
"author": "Nathaniel Anozie", | |
"version": (0, 1), | |
"blender": (2, 79, 0), | |
"location": "3D View", | |
"description": "simple animation arc tracker (package)", | |
"category": "Object", | |
} | |
from . import ui | |
imp.reload(ui) | |
from . import core | |
imp.reload(core) | |
def register(): | |
ui.register() | |
def unregister(): | |
ui.unregister() | |
if __name__ == "__main__": | |
register() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import logging | |
import bpy | |
logger = logging.getLogger(__name__) | |
logging.basicConfig(level=logging.DEBUG) #without this info logs wouldnt show in console | |
class BonePositionExtractor(object): | |
""" | |
#abstraction to get world positions of a pose bone on some previous frames | |
# get previous frame: current frame cf, cf-1, cf-2, cf-t | |
# get previous nth frames: current frame cf - 1*n, cf-2*n, cf-3*n ... cf-t*n | |
#started with what need abstraction to do | |
""" | |
""" | |
import imp | |
testTool = imp.load_source("tool","/Users/Nathaniel/Documents/src_blender/python/animationTools/naArcTracker/core.py") #change to path to python file | |
bpe = testTool.BonePositionExtractor("Armature", "Bone", 10) #currentFrame 10 | |
bpe.getPreviousFrame(3) #should use frames 9,8,7 | |
bpe.getPreviousNthFrame(3,4) #should use frames 6,2,-2 | |
#made up of examples of what results we expect to get | |
""" | |
def __init__(self, armature, bone, currentFrame, includeCurrentFrame=True): | |
""" | |
@param armature (str) name of armature - data object name | |
@param bone (str) name of pose bone | |
@param currentFrame (double) what is current frame | |
@param includeCurrentFrame (bool) whether to include current frame in calculations or not | |
""" | |
self._armature = armature | |
self._bone = bone #todo: generalize to also support a mesh, maybe even a single vertex, or an empty | |
self._currentFrame = currentFrame | |
self._includeCurrentFrame = includeCurrentFrame | |
def getPreviousFrame(self, framesBack=1): | |
"""returns list of world positions of bone starting with previous frame | |
get previous frame: current frame cf, cf-1, cf-2, cf-t | |
@param framesBack (int) how many frames to go back or length of returned list. should be > 0 | |
ex t=1 says return previous frame. t=2 says return previous frame position and next previous frame position | |
ex t=3 and current frame is 10 says return list of positions for frames 9, 8, 7 | |
""" | |
result = [] | |
assert(framesBack>0) | |
curFrame = self._currentFrame | |
framesToQuery = list(range((curFrame-framesBack), curFrame)) | |
framesToQuery.reverse() #so first in list is previous frame | |
if self._includeCurrentFrame: | |
framesToQuery = [curFrame]+framesToQuery | |
#get world position on given frames | |
for frame in framesToQuery: | |
result.append(self._getWorldPositionOfBone(frame)) | |
return result | |
def getPreviousNthFrame(self, length=1, skipFrames=2): | |
"""returns list of world positions of bone of previous nth frames. length of returned list is t | |
@param length (int) how many times to go back or length of returned list. | |
@param skipFrames (int) number of skipped frames. | |
ex: skipFrames=2 and length=2 for a current frame of 10 says return list of positions for frames 8, 6 | |
""" | |
result = [] | |
assert(length>0) | |
assert(skipFrames>0) | |
curFrame = self._currentFrame | |
framesToQuery = list(range(curFrame-length*skipFrames, curFrame, skipFrames)) | |
framesToQuery.reverse() #so first in list is most recent frame of query | |
if self._includeCurrentFrame: | |
framesToQuery = [curFrame]+framesToQuery | |
#get world position on given frames | |
for frame in framesToQuery: | |
result.append(self._getWorldPositionOfBone(frame)) | |
return result | |
def _getWorldPositionOfBone(self, frame): | |
"""get the world position of a pose bone for given frame | |
@param frame (double) frame to query position on | |
""" | |
armature = self._armature | |
bone = self._bone | |
#save scene current frame | |
curFrame = bpy.context.scene.frame_current | |
#get world position of bone | |
#i think works in pose or object modes | |
#go to query frame | |
bpy.context.scene.frame_set(frame) | |
position = tuple(bpy.data.objects[armature].pose.bones[bone].matrix.translation) | |
position = [round(x, 4) for x in position] #round to 4 places may want more accuracy | |
#restore scene current frame | |
bpy.context.scene.frame_set(curFrame) | |
return position | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bpy | |
import bgl | |
import blf | |
from . import core | |
import logging | |
logger = logging.getLogger(__name__) | |
logging.basicConfig(level=logging.DEBUG) #without this info logs wouldnt show in console | |
def drawArcCallback(self, context): | |
""" | |
currently draw positions for a pose bone over a certain number of frames back | |
""" | |
bgl.glEnable(bgl.GL_BLEND) | |
#do the drawing | |
width = 2 | |
color = (0.0, 0.0, 1.0, 0.7) | |
currentFrame = context.scene.frame_current | |
logger.debug("current frame>>>"+str(currentFrame)) | |
bone = self._bone #Bone | |
armature = self._armature #Armature | |
numFramesBack = self._numFramesBack #ex: 3 | |
bpe = core.BonePositionExtractor(armature, bone, currentFrame) | |
allPoints = bpe.getPreviousFrame(framesBack=numFramesBack) | |
#ex: allPoints = [(0.0, 0.0, 0.0), (10.0, 10.0, 15.0)] | |
numPoints = len(allPoints) | |
bgl.glLineWidth(width) | |
bgl.glColor4f(*color) | |
bgl.glBegin(bgl.GL_LINES) | |
for i in range(0,numPoints-1): | |
startPoint = allPoints[i] | |
endPoint = allPoints[i+1] | |
#todo draw spheres between line segments or something like that | |
bgl.glVertex3f(*startPoint) | |
bgl.glVertex3f(*endPoint) | |
#end drawing | |
bgl.glEnd() | |
bgl.glLineWidth(1) | |
bgl.glDisable(bgl.GL_BLEND) | |
bgl.glColor4f(0.0, 0.0, 0.0, 1.0) | |
class naArcTrackerOperator(bpy.types.Operator): | |
bl_idname = "arc.modal_draw" | |
bl_label = "naArcTracker" | |
def modal(self, context, event): | |
#self.report({'INFO'}, event.type) | |
context.area.tag_redraw() #? | |
#how to exit | |
if event.type in {'RIGHTMOUSE', 'ESC'}: | |
bpy.types.SpaceView3D.draw_handler_remove(self._handleArc, 'WINDOW') | |
return {'CANCELLED'} | |
return {'PASS_THROUGH'} #? | |
#return {'RUNNING_MODAL'} #i think using this stall viewport interaction | |
def invoke(self, context, event): | |
if context.area.type == 'VIEW_3D': | |
args = (self, context) #arguments passed to draw callback | |
#todo: support things other than a pose bone | |
self._armature = context.object.name | |
self._bone = context.selected_pose_bones[0].name #only using first selected bone | |
self._numFramesBack = 10 #todo: be able to get this from ui | |
self._handleArc = bpy.types.SpaceView3D.draw_handler_add(drawArcCallback, args, 'WINDOW', 'POST_VIEW') | |
#i think POST_VIEW for drawing in 3d viewport space | |
#i think POST_PIXEL is drawing in 2d pixel space | |
context.window_manager.modal_handler_add(self) | |
return {'RUNNING_MODAL'} | |
else: | |
self.report({'WARNING'}, "requires to be in 3d viewport to create arc tracking") | |
return {'CANCELLED'} | |
def register(): | |
bpy.utils.register_class(naArcTrackerOperator) | |
def unregister(): | |
bpy.utils.unregister_class(naArcTrackerOperator) | |
#inspired by: | |
#https://blender.stackexchange.com/questions/61699/how-to-draw-geometry-in-3d-view-window-with-bgl |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment