forked from micwalk/blender-export-diffusion
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexport_diffusion.py
199 lines (180 loc) · 7.58 KB
/
export_diffusion.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
bl_info = {
"name": "Export Camera Animation to Deforum collab/webui",
"author": "Kewk",
"version": (1, 4, 0),
"blender": (4, 1, 0),
"location": "File > Export > Diffusion String",
"description": "Export camera animations formatted for use in Deforum",
"warning": "",
"wiki_url": "",
"category": "Import-Export",
}
import bpy
from bpy_extras.io_utils import ImportHelper, ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty, IntProperty, FloatProperty
from bpy.types import Operator
from math import degrees
from math import isclose
import json
def roundZero(num, magnitude_thresh=0.00001):
if abs(num) > magnitude_thresh:
return num
else:
return 0
def arr_to_keyframes(arr):
keyframes = ""
for i, val in enumerate(arr):
val = roundZero(val)
last_is_same = i > 0 and isclose(val, roundZero(arr[i-1]))
next_is_same = (i+1) < len(arr) and isclose(val, roundZero(arr[i+1]))
omit = last_is_same and next_is_same
if not omit:
keyframes += f"{i}:({val}),"
return keyframes
def cameras_to_string(context, startFrame, endFrame, cameras, translation_scale=50, output_camcode=True, output_json=False, output_raw_frames=False):
scene = context.scene
currentFrame = scene.frame_current
if len(cameras) == 0:
print("Nothing selected!")
return "No Cameras selected for export"
export_string = ""
for sel in cameras:
scene.frame_set(startFrame)
translation_x = []
translation_y = []
translation_z = []
rotation_3d_x = []
rotation_3d_y = []
rotation_3d_z = []
oldMat = sel.matrix_world.copy()
oldRot = oldMat.to_quaternion()
for frame in range(startFrame+1, endFrame):
scene.frame_set(frame)
newMat = sel.matrix_world.copy()
newRot = newMat.to_quaternion()
worldToLocal = newMat.inverted()
wlRot = worldToLocal.to_quaternion()
posDiff = newMat.to_translation() - oldMat.to_translation()
posDiffLocal = wlRot @ posDiff
translation_x.append(translation_scale*posDiffLocal.x)
translation_y.append(translation_scale*posDiffLocal.y)
translation_z.append(-translation_scale*posDiffLocal.z)
rotDiff = oldRot.rotation_difference(newRot).to_euler("XYZ")
rotation_3d_x.append(degrees(rotDiff.x))
rotation_3d_y.append(degrees(-rotDiff.y))
rotation_3d_z.append(degrees(-rotDiff.z))
oldMat = newMat
oldRot = newRot
if not output_raw_frames:
export_string += f"\nCamera Export: {sel.name}\n"
export_string += f'translation_x = "{arr_to_keyframes(translation_x)}" #@param {{type:"string"}}\n'
export_string += f'translation_y = "{arr_to_keyframes(translation_y)}" #@param {{type:"string"}}\n'
export_string += f'translation_z = "{arr_to_keyframes(translation_z)}" #@param {{type:"string"}}\n'
export_string += f'rotation_3d_x = "{arr_to_keyframes(rotation_3d_x)}" #@param {{type:"string"}}\n'
export_string += f'rotation_3d_y = "{arr_to_keyframes(rotation_3d_y)}" #@param {{type:"string"}}\n'
export_string += f'rotation_3d_z = "{arr_to_keyframes(rotation_3d_z)}" #@param {{type:"string"}}\n'
if output_camcode:
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'
if output_json:
jsondict = {
"translation_x": translation_x,
"translation_y": translation_y,
"translation_z": translation_z,
"rotation_3d_x": rotation_3d_x,
"rotation_3d_y": rotation_3d_y,
"rotation_3d_z": rotation_3d_z
}
export_string += f"JSON:\n {json.dumps(jsondict)}\n"
if output_raw_frames:
raw_frames = {
"translation_x": translation_x,
"translation_y": translation_y,
"translation_z": translation_z,
"rotation_3d_x": rotation_3d_x,
"rotation_3d_y": rotation_3d_y,
"rotation_3d_z": rotation_3d_z
}
for key, arr in raw_frames.items():
raw_frame_str = ""
last_val = None
for i, val in enumerate(arr):
if last_val is None or not isclose(val, last_val):
raw_frame_str += f"{i}:({val}),"
last_val = val
raw_frame_str = raw_frame_str.rstrip(",")
export_string += f"\nRaw frames for {key}:\n{raw_frame_str}\n"
export_string += "\n"
scene.frame_set(currentFrame)
return export_string
def write_camera_data(context, filepath, start, end, cams, scale, output_camcode, output_json, output_raw_frames):
print("running write_camera_data...")
outputString = cameras_to_string(context, start, end, cams, scale, output_camcode, output_json, output_raw_frames)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(f"Export frames {start} - {end}\n")
f.write(outputString)
return {'FINISHED'}
class ExportDiffusionString(Operator, ExportHelper):
bl_idname = "export_scene.diffusion_string"
bl_label = "Export Diffusion String"
filename_ext = ".txt"
filter_glob: StringProperty(
default="*.txt",
options={'HIDDEN'},
maxlen=255,
)
start_frame: IntProperty(
name="Start Frame",
description="Starting frame for the export",
default=1,
min=1,
max=300000
)
end_frame: IntProperty(
name="End Frame",
description="End frame for the export",
default=250,
min=1,
max=300000
)
translation_scale: FloatProperty(
name="Position Scale",
description="Scales camera motion. Higher values make the camera move more",
default=1,
min=0,
max=1000
)
output_camcode: BoolProperty(
name="Output Camcode",
description="Output a code block formatted for use as a camcode input",
default=True
)
output_json: BoolProperty(
name="Output JSON",
description="Output a JSON formatted dictionary",
default=False
)
output_raw_frames: BoolProperty(
name="Output Raw Frames",
description="Output raw translation/rotation values for each frame for each axis",
default=False
)
def execute(self, context):
start = self.start_frame
end = self.end_frame
cams = context.selected_objects
scale = self.translation_scale
camcode = self.output_camcode
json_output = self.output_json
raw_frames = self.output_raw_frames
write_camera_data(context, self.filepath, start, end, cams, scale, camcode, json_output, raw_frames)
return {'FINISHED'}
def menu_func_export(self, context):
self.layout.operator(ExportDiffusionString.bl_idname, text="Diffusion String (.txt)")
def register():
bpy.utils.register_class(ExportDiffusionString)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
def unregister():
bpy.utils.unregister_class(ExportDiffusionString)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
if __name__ == "__main__":
register()