Skip to content

Commit 59a0e57

Browse files
authored
Auto export altered meshes (#157)
* Initial commit of pseuodocode * mesh hash to detect mesh changes * recalculate the mesh hash on load, start of export logic * Basic functionality working Choose between overwriting and saving to different directory. Check for invalid directory * Add other file types, some cleanup
1 parent 9d77d95 commit 59a0e57

File tree

4 files changed

+290
-14
lines changed

4 files changed

+290
-14
lines changed

src/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"name": "Stop motion OBJ",
2626
"description": "Import a sequence of OBJ (or STL or PLY or X3D) files and display them each as a single frame of animation. This add-on also supports the .STL, .PLY, and .X3D file formats.",
2727
"author": "Justin Jensen",
28-
"version": (2, 2, 0, "alpha.18"),
28+
"version": (2, 2, 0, "alpha.19"),
2929
"blender": (2, 83, 0),
3030
"location": "File > Import > Mesh Sequence",
3131
"warning": "",
@@ -37,7 +37,11 @@
3737
SMOKeymaps = []
3838

3939
def register():
40+
bpy.app.handlers.frame_change_pre.append(checkMeshChangesFrameChangePre)
41+
bpy.app.handlers.frame_change_post.append(checkMeshChangesFrameChangePost)
42+
4043
bpy.types.Mesh.inMeshSequence = bpy.props.BoolProperty()
44+
bpy.types.Mesh.meshHash = bpy.props.StringProperty()
4145
bpy.utils.register_class(SequenceVersion)
4246
bpy.utils.register_class(MeshImporter)
4347
bpy.utils.register_class(MeshNameProp)
@@ -63,8 +67,10 @@ def register():
6367
bpy.utils.register_class(ConvertToMeshSequence)
6468
bpy.utils.register_class(DuplicateMeshFrame)
6569
bpy.utils.register_class(SMO_PT_MeshSequencePanel)
70+
# note: the order of the next few panels is the order they appear in the UI
6671
bpy.utils.register_class(SMO_PT_MeshSequencePlaybackPanel)
6772
bpy.utils.register_class(SMO_PT_MeshSequenceStreamingPanel)
73+
bpy.utils.register_class(SMO_PT_MeshSequenceExportPanel)
6874
bpy.utils.register_class(SMO_PT_MeshSequenceAdvancedPanel)
6975
bpy.app.handlers.render_init.append(renderInitHandler)
7076
bpy.app.handlers.render_complete.append(renderCompleteHandler)
@@ -92,6 +98,9 @@ def register():
9298
SMOKeymaps.append((keyMap, keyMapItem))
9399

94100
def unregister():
101+
bpy.app.handlers.frame_change_pre.remove(checkMeshChangesFrameChangePre)
102+
bpy.app.handlers.frame_change_post.remove(checkMeshChangesFrameChangePost)
103+
95104
bpy.app.handlers.load_post.remove(initializeSequences)
96105
bpy.app.handlers.frame_change_pre.remove(updateFrame)
97106
bpy.app.handlers.frame_change_pre.remove(updateFrameSingleMesh)
@@ -111,6 +120,7 @@ def unregister():
111120
bpy.utils.unregister_class(SMO_PT_MeshSequencePanel)
112121
bpy.utils.unregister_class(SMO_PT_MeshSequencePlaybackPanel)
113122
bpy.utils.unregister_class(SMO_PT_MeshSequenceStreamingPanel)
123+
bpy.utils.unregister_class(SMO_PT_MeshSequenceExportPanel)
114124
bpy.utils.unregister_class(SMO_PT_MeshSequenceAdvancedPanel)
115125
bpy.utils.unregister_class(MeshSequenceSettings)
116126
bpy.utils.unregister_class(MeshNameProp)

src/panels.py

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ def draw(self, context):
6767
# keyframed playback
6868
if objSettings.frameMode == '4':
6969
row = col.row()
70-
if objSettings.curMeshIdx <= 0 or objSettings.curMeshIdx > objSettings.numMeshes - 1:
70+
if objSettings.curKeyframeMeshIdx <= 0 or objSettings.curKeyframeMeshIdx > objSettings.numMeshes - 1:
7171
row.alert = True
72-
row.prop(objSettings, "curMeshIdx")
72+
row.prop(objSettings, "curKeyframeMeshIdx")
7373
# all other playback modes
7474
else:
7575
col.prop(objSettings, "startFrame")
@@ -97,6 +97,39 @@ def draw(self, context):
9797
col.prop(objSettings, "streamDuringPlayback")
9898

9999

100+
class SMO_PT_MeshSequenceExportPanel(bpy.types.Panel):
101+
bl_label = 'Export'
102+
bl_parent_id = "OBJ_SEQUENCE_PT_properties"
103+
bl_space_type = 'PROPERTIES'
104+
bl_region_type = 'WINDOW'
105+
bl_options = {'DEFAULT_CLOSED'}
106+
107+
@classmethod
108+
def poll(cls, context):
109+
return context.object.mesh_sequence_settings.initialized == True
110+
111+
def draw(self, context):
112+
layout = self.layout
113+
objSettings = context.object.mesh_sequence_settings
114+
inObjectMode = context.mode == 'OBJECT'
115+
inSculptMode = context.mode == 'SCULPT'
116+
117+
if objSettings.isImported is True:
118+
# non-imported sequences won't have a fileName or dirPath and cannot be exported (for now)
119+
row = layout.row()
120+
row.enabled = inObjectMode or inSculptMode
121+
row.prop(objSettings, "autoExportChanges")
122+
123+
row = layout.row()
124+
row.enabled = inObjectMode or inSculptMode
125+
row.prop(objSettings, "overwriteSrcDir")
126+
127+
row = layout.row()
128+
row.enabled = (inObjectMode or inSculptMode) and objSettings.overwriteSrcDir is False
129+
row.alert = objSettings.exportDir == '' and objSettings.overwriteSrcDir is False
130+
131+
row.prop(objSettings, "exportDir")
132+
100133
class SMO_PT_MeshSequenceAdvancedPanel(bpy.types.Panel):
101134
bl_label = 'Advanced'
102135
bl_parent_id = "OBJ_SEQUENCE_PT_properties"
@@ -139,6 +172,8 @@ def draw(self, context):
139172
row = layout.row()
140173
row.enabled = inObjectMode
141174
row.operator("ms.bake_sequence")
175+
176+
142177

143178
row = layout.row()
144179
row.enabled = inObjectMode
@@ -455,11 +490,11 @@ def execute(self, context):
455490
msObj.mesh_sequence_settings.frameMode = '4'
456491

457492
# create a keyframe for this mesh at the current frame
458-
msObj.mesh_sequence_settings.curMeshIdx = 1
459-
msObj.keyframe_insert(data_path='mesh_sequence_settings.curMeshIdx', frame=context.scene.frame_current)
493+
msObj.mesh_sequence_settings.curKeyframeMeshIdx = 1
494+
msObj.keyframe_insert(data_path='mesh_sequence_settings.curKeyframeMeshIdx', frame=context.scene.frame_current)
460495

461496
# make the interpolation constant for the first keyframe
462-
meshIdxCurve = next((curve for curve in msObj.animation_data.action.fcurves if 'curMeshIdx' in curve.data_path), None)
497+
meshIdxCurve = next((curve for curve in msObj.animation_data.action.fcurves if 'curKeyframeMeshIdx' in curve.data_path), None)
463498
keyAtFrame = next((keyframe for keyframe in meshIdxCurve.keyframe_points if keyframe.co.x == context.scene.frame_current), None)
464499
keyAtFrame.interpolation = 'CONSTANT'
465500

@@ -489,8 +524,8 @@ def execute(self, context):
489524
self.report({'ERROR'}, "The selected object is not a mesh sequence")
490525
return {'CANCELLED'}
491526

492-
# if the object doesn't have a 'curMeshIdx' fcurve, we can't add a mesh to it
493-
meshIdxCurve = next((curve for curve in obj.animation_data.action.fcurves if 'curMeshIdx' in curve.data_path), None)
527+
# if the object doesn't have a 'curKeyframeMeshIdx' fcurve, we can't add a mesh to it
528+
meshIdxCurve = next((curve for curve in obj.animation_data.action.fcurves if 'curKeyframeMeshIdx' in curve.data_path), None)
494529
if meshIdxCurve is None:
495530
self.report({'ERROR'}, "The selected mesh sequence has no keyframe curve")
496531
return {'CANCELLED'}
@@ -510,8 +545,8 @@ def execute(self, context):
510545
meshIdx = addMeshToSequence(obj, newMesh)
511546

512547
# add a new keyframe at this frame number for the new mesh
513-
obj.mesh_sequence_settings.curMeshIdx = meshIdx
514-
obj.keyframe_insert(data_path='mesh_sequence_settings.curMeshIdx', frame=context.scene.frame_current)
548+
obj.mesh_sequence_settings.curKeyframeMeshIdx = meshIdx
549+
obj.keyframe_insert(data_path='mesh_sequence_settings.curKeyframeMeshIdx', frame=context.scene.frame_current)
515550

516551
# make the interpolation constant for this keyframe
517552
newKeyAtFrame = next((keyframe for keyframe in meshIdxCurve.keyframe_points if keyframe.co.x == context.scene.frame_current), None)

0 commit comments

Comments
 (0)