Skip to content

Commit 5d4e0e6

Browse files
committed
Key change: fix inverted rotation angles on the Y and Z axis.
Fixed bug where you are unable to change the exported frame range. Also added two new export formats. One json that just exports the full array, and one in the cam_code for the Pharmapsychotic animation preview notebook.
1 parent 0a5d7f5 commit 5d4e0e6

File tree

1 file changed

+50
-16
lines changed

1 file changed

+50
-16
lines changed

export_diffusion.py

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
bl_info = {
22
"name": "Export Camera Animation to Diffusion Notebook String",
33
"author": "Michael Walker (@mwalk10)",
4-
"version": (1, 0, 0),
4+
"version": (1, 1, 4),
55
"blender": (3, 3, 0),
66
"location": "File > Export > Diffusion Notebook String",
7-
"description": "Export camera animations in a format for use in Deforum diffusion collab notebook animations.",
7+
"description": "Export camera animations formatted for use in Deforum diffusion collab notebook animations.",
88
"warning": "",
99
"wiki_url": "",
1010
"category": "Import-Export",
@@ -16,22 +16,29 @@
1616
from mathutils import Vector
1717
from bpy import context
1818
from math import isclose
19+
import json
1920

20-
21+
def roundZero(num, magnitude_thresh = 0.00001):
22+
if abs(num) > magnitude_thresh:
23+
return num
24+
else:
25+
return 0
26+
2127
def arr_to_keyframes(arr):
2228
keyframes = ""
2329
for i, val in enumerate(arr):
30+
val = roundZero(val)
2431
#if we previously had a zero, then we can stop emitting zeroes until right before the next nonzero
25-
last_is_same = i > 0 and isclose(val, arr[i-1])
26-
next_is_same = (i+1) < len(arr) and isclose(val, arr[i+1])
32+
last_is_same = i > 0 and isclose(val, roundZero(arr[i-1]))
33+
next_is_same = (i+1) < len(arr) and isclose(val, roundZero(arr[i+1]))
2734

2835
omit = last_is_same and next_is_same
2936

3037
if not omit:
3138
keyframes += f"{i}:({val}),"
3239
return keyframes
3340

34-
def cameras_to_string(context, startFrame, endFrame, cameras, translation_scale = 50):
41+
def cameras_to_string(context, startFrame, endFrame, cameras, translation_scale = 50, output_camcode = True, output_json = False,):
3542
# get the current selection
3643
scene = context.scene
3744
currentFrame = scene.frame_current
@@ -76,31 +83,47 @@ def cameras_to_string(context, startFrame, endFrame, cameras, translation_scale
7683
translation_z.append(-translation_scale*posDiffLocal.z)
7784

7885
rotDiff = oldRot.rotation_difference(newRot).to_euler("XYZ")
86+
7987
rotation_3d_x.append(degrees(rotDiff.x))
80-
rotation_3d_y.append(degrees(rotDiff.y))
81-
rotation_3d_z.append(degrees(rotDiff.z))
88+
rotation_3d_y.append(degrees(-rotDiff.y))
89+
rotation_3d_z.append(degrees(-rotDiff.z))
8290

8391
oldMat = newMat
8492
oldRot = newRot
8593

8694
#Done looping over frames, now to format for print
8795
export_string += f"\nCamera Export: {sel.name}\n"
88-
96+
97+
8998
export_string += f'translation_x = "{arr_to_keyframes(translation_x)}" #@param {{type:"string"}}\n'
9099
export_string += f'translation_y = "{arr_to_keyframes(translation_y)}" #@param {{type:"string"}}\n'
91100
export_string += f'translation_z = "{arr_to_keyframes(translation_z)}" #@param {{type:"string"}}\n'
92101
export_string += f'rotation_3d_x = "{arr_to_keyframes(rotation_3d_x)}" #@param {{type:"string"}}\n'
93102
export_string += f'rotation_3d_y = "{arr_to_keyframes(rotation_3d_y)}" #@param {{type:"string"}}\n'
94103
export_string += f'rotation_3d_z = "{arr_to_keyframes(rotation_3d_z)}" #@param {{type:"string"}}\n'
104+
105+
if output_camcode:
106+
export_string += f'cam_code:\n(translation_x,translation_y,translation_z,rotation_3d_x,rotation_3d_y,rotation_3d_z) = ("{arr_to_keyframes(translation_x)}", "{arr_to_keyframes(translation_y)}", "{arr_to_keyframes(translation_z)}", "{arr_to_keyframes(rotation_3d_x)}", "{arr_to_keyframes(rotation_3d_y)}", "{arr_to_keyframes(rotation_3d_z)}")\n'
107+
108+
if output_json:
109+
jsondict = {
110+
"translation_x" : translation_x,
111+
"translation_y" : translation_y,
112+
"translation_z" : translation_z,
113+
"rotation_3d_x" : rotation_3d_x,
114+
"rotation_3d_y" : rotation_3d_y,
115+
"rotation_3d_z" : rotation_3d_z}
116+
export_string += f"JSON:\n {json.dumps(jsondict)}\n"
117+
95118
export_string += "\n"
96119

97120
#Done saving all cameras, restore original animation frame
98121
scene.frame_set(currentFrame)
99122
return export_string
100123

101-
def write_camera_data(context, filepath, start, end, cams, scale):
124+
def write_camera_data(context, filepath, start, end, cams, scale, output_camcode, output_json):
102125
print("running write_camera_data...")
103-
outputString = cameras_to_string(context, start, end, cams, scale)
126+
outputString = cameras_to_string(context, start, end, cams, scale, output_camcode, output_json)
104127
with open(filepath, 'w', encoding='utf-8') as f:
105128
f.write(f"Export frames {start} - {end}\n")
106129
f.write(f"Export cameras {[c.name for c in cams]}\n")
@@ -139,8 +162,11 @@ class ExportDiffusionString(Operator, ExportHelper):
139162
# default='3D',
140163
# )
141164

142-
frame_start: IntProperty(name="Start", default=0)
143-
frame_end: IntProperty(name="End", default=100)#bpy.context.scene.frame_end
165+
output_json: BoolProperty(name="Output JSON", default=False)
166+
output_cam_code: BoolProperty(name="Output cam_code", default=True)
167+
168+
frame_start: IntProperty(name="Start", default=-1)
169+
frame_end: IntProperty(name="End", default=-1)#bpy.context.scene.frame_end
144170

145171
which_cams: EnumProperty(
146172
name="Which Cams",
@@ -167,8 +193,10 @@ def draw(self, context):
167193

168194
row = layout.row()
169195
row.label(text="Frames")
170-
self.frame_start = bpy.context.scene.frame_start
171-
self.frame_end = bpy.context.scene.frame_end
196+
if self.frame_start == -1:
197+
self.frame_start = bpy.context.scene.frame_start
198+
if self.frame_end == -1:
199+
self.frame_end = bpy.context.scene.frame_end
172200
row.prop(self, "frame_start")
173201
row.prop(self, "frame_end")
174202

@@ -177,6 +205,11 @@ def draw(self, context):
177205
row = layout.row()
178206
row.prop(self, "translation_scale")
179207

208+
row = layout.row()
209+
row.prop(self, "output_cam_code")
210+
row = layout.row()
211+
row.prop(self, "output_json")
212+
180213
def execute(self, context):
181214
export_cams = []
182215
if self.which_cams == "ACTIVE":
@@ -185,7 +218,8 @@ def execute(self, context):
185218
export_cams = [cam for cam in context.selected_objects if cam.type == 'CAMERA']
186219
elif self.which_cams == "ALL":
187220
export_cams = [cam for cam in context.scene.objects if cam.type == 'CAMERA']
188-
return write_camera_data(context, self.filepath, self.frame_start, self.frame_end, export_cams, self.translation_scale)
221+
return write_camera_data(context, self.filepath, self.frame_start, self.frame_end, export_cams,
222+
self.translation_scale, self.output_cam_code, self.output_json)
189223

190224

191225
# Only needed if you want to add into a dynamic menu

0 commit comments

Comments
 (0)