Skip to content

Commit

Permalink
Merge branch 'merge_animations'
Browse files Browse the repository at this point in the history
  • Loading branch information
julienduroure committed Sep 11, 2019
2 parents 368c804 + ef4e9e1 commit 8a38eca
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 26 deletions.
8 changes: 8 additions & 0 deletions addons/io_scene_gltf2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ def __init__(self):
default=False
)

export_nla_strips = BoolProperty(
name='NLA Strips',
description='Export NLA Strip animations',
default=True
)

export_current_frame = BoolProperty(
name='Use Current Frame',
description='Export the scene in the current animation frame',
Expand Down Expand Up @@ -382,6 +388,7 @@ def execute(self, context):
if self.export_animations:
export_settings['gltf_frame_range'] = self.export_frame_range
export_settings['gltf_force_sampling'] = self.export_force_sampling
export_settings['gltf_nla_strips'] = self.export_nla_strips
else:
export_settings['gltf_frame_range'] = False
export_settings['gltf_move_keyframes'] = False
Expand Down Expand Up @@ -693,6 +700,7 @@ def draw(self, context):
layout.prop(operator, 'export_frame_range')
layout.prop(operator, 'export_frame_step')
layout.prop(operator, 'export_force_sampling')
layout.prop(operator, 'export_nla_strips')


class GLTF_PT_export_animation_shapekeys(bpy.types.Panel):
Expand Down
58 changes: 56 additions & 2 deletions addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import bpy

from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.io.com.gltf2_io_debug import print_console
from io_scene_gltf2.blender.exp import gltf2_blender_gather_nodes
from io_scene_gltf2.blender.exp import gltf2_blender_gather_animations
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
Expand Down Expand Up @@ -60,12 +61,65 @@ def __gather_scene(blender_scene, export_settings):

def __gather_animations(blender_scene, export_settings):
animations = []
merged_tracks = {}

for blender_object in blender_scene.objects:
# First check if this object is exported or not. Do not export animation of not exported object
obj_node = gltf2_blender_gather_nodes.gather_node(blender_object, blender_scene, export_settings)
if obj_node is not None:
animations += gltf2_blender_gather_animations.gather_animations(blender_object, export_settings)
return animations
animations_, merged_tracks = gltf2_blender_gather_animations.gather_animations(blender_object, merged_tracks, len(animations), export_settings)
animations += animations_

if export_settings['gltf_nla_strips'] is False:
# Fake an animation witha all animations of the scene
merged_tracks = {}
merged_tracks['Animation'] = []
for idx, animation in enumerate(animations):
merged_tracks['Animation'].append(idx)


to_delete_idx = []
for merged_anim_track in merged_tracks.keys():
if len(merged_tracks[merged_anim_track]) < 2:
continue

base_animation_idx = None
offset_sampler = 0

for idx, anim_idx in enumerate(merged_tracks[merged_anim_track]):
if idx == 0:
base_animation_idx = anim_idx
animations[anim_idx].name = merged_anim_track
already_animated = []
for channel in animations[anim_idx].channels:
already_animated.append((channel.target.node, channel.target.path))
continue

to_delete_idx.append(anim_idx)

offset_sampler = len(animations[base_animation_idx].samplers)
for sampler in animations[anim_idx].samplers:
animations[base_animation_idx].samplers.append(sampler)

for channel in animations[anim_idx].channels:
if (channel.target.node, channel.target.path) in already_animated:
print_console("WARNING", "Some strips have same channel animation ({}), on node {} !".format(channel.target.path, channel.target.node.name))
continue
animations[base_animation_idx].channels.append(channel)
animations[base_animation_idx].channels[-1].sampler = animations[base_animation_idx].channels[-1].sampler + offset_sampler
already_animated.append((channel.target.node, channel.target.path))

new_animations = []
if len(to_delete_idx) != 0:
for idx, animation in enumerate(animations):
if idx in to_delete_idx:
continue
new_animations.append(animation)
else:
new_animations = animations


return new_animations


def __gather_extras(blender_object, export_settings):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,21 @@
from io_scene_gltf2.io.com.gltf2_io_debug import print_console


def gather_animations(blender_object: bpy.types.Object, export_settings) -> typing.List[gltf2_io.Animation]:
def gather_animations(blender_object: bpy.types.Object,
tracks: typing.Dict[str, typing.List[int]],
offset: int,
export_settings) -> typing.Tuple[typing.List[gltf2_io.Animation], typing.Dict[str, typing.List[int]]]:
"""
Gather all animations which contribute to the objects property.
Gather all animations which contribute to the objects property, and corresponding track names
:param blender_object: The blender object which is animated
:param export_settings:
:return: A list of glTF2 animations
:return: A list of glTF2 animations and tracks
"""
animations = []

# Collect all 'actions' affecting this object. There is a direct mapping between blender actions and glTF animations
blender_actions = __get_blender_actions(blender_object)
blender_actions = __get_blender_actions(blender_object, export_settings)

# save the current active action of the object, if any
# We will restore it after export
Expand All @@ -40,7 +43,7 @@ def gather_animations(blender_object: bpy.types.Object, export_settings) -> typi
current_action = blender_object.animation_data.action

# Export all collected actions.
for blender_action in blender_actions:
for blender_action, track_name in blender_actions:

# Set action as active, to be able to bake if needed
if blender_object.animation_data: # Not for shapekeys!
Expand All @@ -62,12 +65,20 @@ def gather_animations(blender_object: bpy.types.Object, export_settings) -> typi
if animation is not None:
animations.append(animation)

# Store data for merging animation later
if track_name is not None: # Do not take into account animation not in NLA
# Do not take into account default NLA track names
if not (track_name.startswith("NlaTrack") or track_name.startswith("[Action Stash]")):
if track_name not in tracks.keys():
tracks[track_name] = []
tracks[track_name].append(offset + len(animations)-1) # Store index of animation in animations

# Restore current action
if blender_object.animation_data:
if blender_object.animation_data.action is not None and current_action is not None and blender_object.animation_data.action.name != current_action.name:
blender_object.animation_data.action = current_action

return animations
return animations, tracks


def __gather_animation(blender_action: bpy.types.Action,
Expand Down Expand Up @@ -172,23 +183,28 @@ def __append_unique_and_get_index(l: typing.List[T], item: T):
animation.channels[i].sampler = __append_unique_and_get_index(animation.samplers, channel.sampler)


def __get_blender_actions(blender_object: bpy.types.Object
) -> typing.List[bpy.types.Action]:
def __get_blender_actions(blender_object: bpy.types.Object,
export_settings
) -> typing.List[typing.Tuple[bpy.types.Action, str]]:
blender_actions = []
blender_tracks = {}

if blender_object.animation_data is not None:
# Collect active action.
if blender_object.animation_data.action is not None:
blender_actions.append(blender_object.animation_data.action)
blender_tracks[blender_object.animation_data.action.name] = None

# Collect associated strips from NLA tracks.
for track in blender_object.animation_data.nla_tracks:
# Multi-strip tracks do not export correctly yet (they need to be baked),
# so skip them for now and only write single-strip tracks.
if track.strips is None or len(track.strips) != 1:
continue
for strip in [strip for strip in track.strips if strip.action is not None]:
blender_actions.append(strip.action)
if export_settings['gltf_nla_strips'] is True:
for track in blender_object.animation_data.nla_tracks:
# Multi-strip tracks do not export correctly yet (they need to be baked),
# so skip them for now and only write single-strip tracks.
if track.strips is None or len(track.strips) != 1:
continue
for strip in [strip for strip in track.strips if strip.action is not None]:
blender_actions.append(strip.action)
blender_tracks[strip.action.name] = track.name # Always set after possible active action -> None will be overwrite

if blender_object.type == "MESH" \
and blender_object.data is not None \
Expand All @@ -197,16 +213,19 @@ def __get_blender_actions(blender_object: bpy.types.Object

if blender_object.data.shape_keys.animation_data.action is not None:
blender_actions.append(blender_object.data.shape_keys.animation_data.action)

for track in blender_object.data.shape_keys.animation_data.nla_tracks:
# Multi-strip tracks do not export correctly yet (they need to be baked),
# so skip them for now and only write single-strip tracks.
if track.strips is None or len(track.strips) != 1:
continue
for strip in track.strips:
blender_actions.append(strip.action)
blender_tracks[blender_object.data.shape_keys.animation_data.action.name] = None

if export_settings['gltf_nla_strips'] is True:
for track in blender_object.data.shape_keys.animation_data.nla_tracks:
# Multi-strip tracks do not export correctly yet (they need to be baked),
# so skip them for now and only write single-strip tracks.
if track.strips is None or len(track.strips) != 1:
continue
for strip in track.strips:
blender_actions.append(strip.action)
blender_tracks[strip.action.name] = track.name # Always set after possible active action -> None will be overwrite

# Remove duplicate actions.
blender_actions = list(set(blender_actions))

return blender_actions
return [(blender_action, blender_tracks[blender_action.name]) for blender_action in blender_actions]

0 comments on commit 8a38eca

Please sign in to comment.