Skip to content

Commit 9ae28c8

Browse files
committed
rotate blender tracks by 90 degrees around x-axis.
1 parent 03269bf commit 9ae28c8

File tree

2 files changed

+75
-48
lines changed

2 files changed

+75
-48
lines changed

utils/exporters/blender/addons/io_three/exporter/api/object.py

Lines changed: 74 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -131,82 +131,109 @@ def material(obj):
131131
except IndexError:
132132
pass
133133

134-
QUAT_CONVERSION = axis_conversion(from_forward='Y', from_up='Z', to_forward='Z', to_up='Y')
135-
136-
def __swap_quaternions(track):
137-
for t in track:
138-
a = t["value"]
139-
q = mathutils.Quaternion(a)
140-
q = (QUAT_CONVERSION*q.to_matrix()).to_quaternion()
141-
a[0] =-q.x
142-
a[1] =-q.y
143-
a[2] =-q.z
144-
a[3] =-q.w
145-
pass
146-
147-
def __swap_vector3(track):
148-
for t in track:
149-
v = t["value"]
150-
tmp = v[1]
151-
v[1] = v[2]
152-
v[2] = tmp
153-
pass
154-
155-
def __parse_tracked_vector(fcurves, start_index, nb_curves):
156-
track = []
134+
def extract_time(fcurves, start_index):
135+
time = []
157136
for xx in fcurves[start_index].keyframe_points:
158-
track.append({ "time": xx.co.x, "value": [xx.co.y] })
137+
time.append(xx.co.x)
138+
return time
139+
140+
def merge_sorted_lists(l1, l2):
141+
sorted_list = []
142+
l1 = l1[:]
143+
l2 = l2[:]
144+
while (l1 and l2):
145+
h1 = l1[0]
146+
h2 = l2[0]
147+
if h1 == h2:
148+
sorted_list.append(h1)
149+
l1.pop(0)
150+
l2.pop(0)
151+
elif h1 < h2:
152+
l1.pop(0)
153+
sorted_list.append(h1)
154+
else:
155+
l2.pop(0)
156+
sorted_list.append(h2)
157+
# Add the remaining of the lists
158+
sorted_list.extend(l1 if l1 else l2)
159+
return sorted_list
159160

160-
swapFunction = __swap_vector3 if nb_curves == 3 else __swap_quaternions
161+
def appendVec3(track, time, vec3):
162+
track.append({ "time": time, "value": [ vec3.x, vec3.y, vec3.z ] })
161163

162-
nb_curves += start_index
163-
start_index += 1
164-
while start_index < nb_curves:
165-
i = 0
166-
for xx in fcurves[start_index].keyframe_points:
167-
track[i]["value"].append(xx.co.y)
168-
i += 1
169-
start_index += 1
170-
swapFunction(track)
171-
return track
164+
def appendQuat(track, time, quat):
165+
track.append({ "time": time, "value": [ quat.x, quat.y, quat.z, quat.w ] })
172166

173-
# trackable transform fields ( <output field>, <nb fcurve>, <type> )
167+
# trackable transform fields ( <output field>, <nb fcurve> )
174168
TRACKABLE_FIELDS = {
175169
"location": ( ".position", 3, "vector3" ),
176170
"scale": ( ".scale", 3, "vector3" ),
177171
"rotation_euler": ( ".rotation", 3, "vector3" ),
178172
"rotation_quaternion": ( ".quaternion", 4, "quaternion" )
179173
}
174+
EXPORTED_TRACKABLE_FIELDS = [ "location", "scale", "rotation_quaternion" ]
180175

181176
@_object
182-
def animated_xform(obj):
177+
def animated_xform(obj, options):
183178
fcurves = obj.animation_data
184179
if not fcurves:
185-
return {}
180+
return []
186181
fcurves = fcurves.action.fcurves
187182

188183
tracks = []
189184
i = 0
190185
nb_curves = len(fcurves)
186+
187+
# extract unique frames
188+
times = None
191189
while i < nb_curves:
192190
field_info = TRACKABLE_FIELDS.get(fcurves[i].data_path)
193191
if field_info:
194-
nb_curves_local = field_info[1]
195-
tracks.append({
196-
constants.NAME: field_info[0],
197-
constants.TYPE: field_info[2],
198-
constants.KEYS: __parse_tracked_vector(fcurves, i, nb_curves_local)
199-
})
200-
i += nb_curves_local
192+
newTimes = extract_time(fcurves, i)
193+
times = merge_sorted_lists(times, newTimes) if times else newTimes # merge list
194+
i += field_info[1]
201195
else:
202196
i += 1
203197

204-
animation = [{
198+
# init tracks
199+
track_loc = []
200+
for fld in EXPORTED_TRACKABLE_FIELDS:
201+
field_info = TRACKABLE_FIELDS[fld]
202+
track = []
203+
track_loc.append(track)
204+
tracks.append({
205+
constants.NAME: field_info[0],
206+
constants.TYPE: field_info[2],
207+
constants.KEYS: track
208+
})
209+
210+
# track arrays
211+
track_sca = track_loc[1]
212+
track_qua = track_loc[2]
213+
track_loc = track_loc[0]
214+
use_inverted = options.get(constants.HIERARCHY, False) and obj.parent
215+
216+
# for each frame
217+
inverted_fallback = mathutils.Matrix() if use_inverted else None
218+
convert_matrix = AXIS_CONVERSION # matrix to convert the exported matrix
219+
original_frame = context.scene.frame_current
220+
for time in times:
221+
context.scene.frame_set(time, 0.0)
222+
if use_inverted: # need to use the inverted, parent matrix might have chance
223+
convert_matrix = obj.parent.matrix_world.inverted(inverted_fallback)
224+
wm = convert_matrix * obj.matrix_world
225+
appendVec3(track_loc, time, wm.to_translation())
226+
appendVec3(track_sca, time, wm.to_scale() )
227+
appendQuat(track_qua, time, wm.to_quaternion() )
228+
context.scene.frame_set(original_frame, 0.0) # restore to original frame
229+
230+
# TODO: remove duplicated key frames
231+
232+
return [{
205233
constants.KEYFRAMES: tracks,
206234
constants.FPS: context.scene.render.fps,
207235
constants.NAME: obj.name
208236
}]
209-
return animation
210237

211238
@_object
212239
def mesh(obj, options):

utils/exporters/blender/addons/io_three/exporter/object.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def _node_setup(self):
124124
no_anim = (None, False, constants.OFF)
125125
if self.options.get(constants.KEYFRAMES) not in no_anim:
126126
logger.info("Export Transform Animation for %s", self.node)
127-
self[constants.CLIPS] = api.object.animated_xform(self.node)
127+
self[constants.CLIPS] = api.object.animated_xform(self.node, self.options)
128128

129129
if self.options.get(constants.HIERARCHY, False):
130130
for child in api.object.children(self.node, self.scene.valid_types):

0 commit comments

Comments
 (0)