[Bf-extensions-cvs] [14f1c99e] master: glTF exporter: Merge animations into glTF animation(s) based on NLA track names

Julien Duroure noreply at git.blender.org
Wed Sep 11 22:14:56 CEST 2019


Commit: 14f1c99e96adad81aea2a5a77eb51d22b96c9618
Author: Julien Duroure
Date:   Wed Sep 11 22:13:41 2019 +0200
Branches: master
https://developer.blender.org/rBA14f1c99e96adad81aea2a5a77eb51d22b96c9618

glTF exporter: Merge animations into glTF animation(s) based on NLA track names

Option "NLA tracks" is active by default. If disable, all active action of scene will be merge into a single glTF animation

===================================================================

M	io_scene_gltf2/__init__.py
M	io_scene_gltf2/blender/exp/gltf2_blender_gather.py
M	io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py

===================================================================

diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index 20f960ea..3460c27c 100755
--- a/io_scene_gltf2/__init__.py
+++ b/io_scene_gltf2/__init__.py
@@ -15,7 +15,7 @@
 bl_info = {
     'name': 'glTF 2.0 format',
     'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
-    "version": (0, 9, 61),
+    "version": (0, 9, 62),
     'blender': (2, 81, 6),
     'location': 'File > Import-Export',
     'description': 'Import-Export as glTF 2.0',
@@ -249,6 +249,12 @@ class ExportGLTF2_Base:
         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',
@@ -382,6 +388,7 @@ class ExportGLTF2_Base:
         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
@@ -631,6 +638,7 @@ class GLTF_PT_export_animation_export(bpy.types.Panel):
         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):
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
index 4d28a4bd..92fa9ce2 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py
@@ -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
@@ -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):
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
index 41a25f57..de205913 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
@@ -20,18 +20,21 @@ from io_scene_gltf2.blender.exp import gltf2_blender_gather_animation_channels
 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
@@ -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!
@@ -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,
@@ -172,23 +183,28 @@ def __link_samplers(animation: gltf2_io.Animation, export_settings):
         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 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list