Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Duroure <julien.duroure@gmail.com>2019-09-17 23:18:19 +0300
committerJulien Duroure <julien.duroure@gmail.com>2019-09-17 23:18:43 +0300
commit87ffe55fd5f704fd981b678f94f3570268025955 (patch)
tree54bba434df051de9b6de58683443978e394af784 /io_scene_gltf2/blender/imp
parent12c0374e16ebe0dc0953bfad720b3d8202c71c7f (diff)
glTF importer: better multi-object animation management
Diffstat (limited to 'io_scene_gltf2/blender/imp')
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_animation.py32
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py69
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_animation_node.py203
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_animation_utils.py28
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_animation_weight.py109
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_gltf.py29
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_scene.py21
7 files changed, 267 insertions, 224 deletions
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_animation.py b/io_scene_gltf2/blender/imp/gltf2_blender_animation.py
index 93014d00..6394145d 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_animation.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation.py
@@ -12,8 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import bpy
+
from .gltf2_blender_animation_bone import BlenderBoneAnim
from .gltf2_blender_animation_node import BlenderNodeAnim
+from .gltf2_blender_animation_weight import BlenderWeightAnim
+from .gltf2_blender_animation_utils import restore_animation_on_object
class BlenderAnimation():
@@ -28,32 +32,28 @@ class BlenderAnimation():
BlenderBoneAnim.anim(gltf, anim_idx, node_idx)
else:
BlenderNodeAnim.anim(gltf, anim_idx, node_idx)
+ BlenderWeightAnim.anim(gltf, anim_idx, node_idx)
if gltf.data.nodes[node_idx].children:
for child in gltf.data.nodes[node_idx].children:
BlenderAnimation.anim(gltf, anim_idx, child)
@staticmethod
- def stash_action(gltf, anim_idx, node_idx, action_name):
+ def restore_animation(gltf, node_idx, animation_name):
+ """Restores the actions for an animation by its track name on
+ the subtree starting at node_idx."""
+ node = gltf.data.nodes[node_idx]
- if gltf.data.nodes[node_idx].is_joint:
- BlenderBoneAnim.stash_action(gltf, anim_idx, node_idx, action_name)
+ if node.is_joint:
+ obj = bpy.data.objects[gltf.data.skins[node.skin_id].blender_armature_name]
else:
- BlenderNodeAnim.stash_action(gltf, anim_idx, node_idx, action_name)
-
- if gltf.data.nodes[node_idx].children:
- for child in gltf.data.nodes[node_idx].children:
- BlenderAnimation.stash_action(gltf, anim_idx, child, action_name)
+ obj = bpy.data.objects[node.blender_object]
- @staticmethod
- def restore_last_action(gltf, node_idx):
-
- if gltf.data.nodes[node_idx].is_joint:
- BlenderBoneAnim.restore_last_action(gltf, node_idx)
- else:
- BlenderNodeAnim.restore_last_action(gltf, node_idx)
+ restore_animation_on_object(obj, animation_name)
+ if obj.data and hasattr(obj.data, 'shape_keys'):
+ restore_animation_on_object(obj.data.shape_keys, animation_name)
if gltf.data.nodes[node_idx].children:
for child in gltf.data.nodes[node_idx].children:
- BlenderAnimation.restore_last_action(gltf, child)
+ BlenderAnimation.restore_animation(gltf, child, animation_name)
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py b/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
index ef88bc37..74b907ae 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation_bone.py
@@ -18,7 +18,7 @@ from mathutils import Matrix
from ..com.gltf2_blender_conversion import loc_gltf_to_blender, quaternion_gltf_to_blender, scale_to_matrix
from ...io.imp.gltf2_io_binary import BinaryData
-from .gltf2_blender_animation_utils import simulate_stash, restore_last_action
+from .gltf2_blender_animation_utils import simulate_stash
class BlenderBoneAnim():
@@ -41,31 +41,6 @@ class BlenderBoneAnim():
kf.interpolation = 'LINEAR'
@staticmethod
- def stash_action(gltf, anim_idx, node_idx, action_name):
- node = gltf.data.nodes[node_idx]
- obj = bpy.data.objects[gltf.data.skins[node.skin_id].blender_armature_name]
-
- if anim_idx not in node.animations.keys():
- return
-
- if (obj.name, action_name) in gltf.actions_stashed.keys():
- return
-
- start_frame = bpy.context.scene.frame_start
-
- animation_name = gltf.data.animations[anim_idx].name
- simulate_stash(obj, animation_name, bpy.data.actions[action_name], start_frame)
-
- gltf.actions_stashed[(obj.name, action_name)] = True
-
- @staticmethod
- def restore_last_action(gltf, node_idx):
- node = gltf.data.nodes[node_idx]
- obj = bpy.data.objects[gltf.data.skins[node.skin_id].blender_armature_name]
-
- restore_last_action(obj)
-
- @staticmethod
def parse_translation_channel(gltf, node, obj, bone, channel, animation):
"""Manage Location animation."""
blender_path = "pose.bones[" + json.dumps(bone.name) + "].location"
@@ -239,7 +214,8 @@ class BlenderBoneAnim():
def anim(gltf, anim_idx, node_idx):
"""Manage animation."""
node = gltf.data.nodes[node_idx]
- obj = bpy.data.objects[gltf.data.skins[node.skin_id].blender_armature_name]
+ blender_armature_name = gltf.data.skins[node.skin_id].blender_armature_name
+ obj = bpy.data.objects[blender_armature_name]
bone = obj.pose.bones[node.blender_bone_name]
if anim_idx not in node.animations.keys():
@@ -247,38 +223,16 @@ class BlenderBoneAnim():
animation = gltf.data.animations[anim_idx]
- if animation.name:
- name = animation.name + "_" + obj.name
- else:
- name = "Animation_" + str(anim_idx) + "_" + obj.name
- if len(name) >= 63:
- # Name is too long to be kept, we are going to keep only animation name for now
- name = animation.name
- if len(name) >= 63:
- # Very long name!
- name = "Animation_" + str(anim_idx)
- if name not in bpy.data.actions:
+ action = gltf.arma_cache.get(blender_armature_name)
+ if not action:
+ name = animation.track_name + "_" + obj.name
action = bpy.data.actions.new(name)
- else:
- if name in gltf.animation_managed:
- # multiple animation with same name in glTF file
- # Create a new action with new name if needed
- if name in gltf.current_animation_names.keys():
- action = bpy.data.actions[gltf.current_animation_names[name]]
- name = gltf.current_animation_names[name]
- else:
- action = bpy.data.actions.new(name)
- else:
- action = bpy.data.actions[name]
- # Check if this action has some users.
- # If no user (only 1 indeed), that means that this action must be deleted
- # (is an action from a deleted object)
- if action.users == 1:
- bpy.data.actions.remove(action)
- action = bpy.data.actions.new(name)
+ gltf.needs_stash.append((obj, animation.track_name, action))
+ gltf.arma_cache[blender_armature_name] = action
+
if not obj.animation_data:
obj.animation_data_create()
- obj.animation_data.action = bpy.data.actions[action.name]
+ obj.animation_data.action = action
for channel_idx in node.animations[anim_idx]:
channel = animation.channels[channel_idx]
@@ -292,6 +246,3 @@ class BlenderBoneAnim():
elif channel.target.path == "scale":
BlenderBoneAnim.parse_scale_channel(gltf, node, obj, bone, channel, animation)
- if action.name not in gltf.current_animation_names.keys():
- gltf.current_animation_names[name] = action.name
-
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_animation_node.py b/io_scene_gltf2/blender/imp/gltf2_blender_animation_node.py
index cf570a20..0b020567 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_animation_node.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation_node.py
@@ -18,7 +18,7 @@ from mathutils import Vector
from ..com.gltf2_blender_conversion import loc_gltf_to_blender, quaternion_gltf_to_blender, scale_gltf_to_blender
from ..com.gltf2_blender_conversion import correction_rotation
from ...io.imp.gltf2_io_binary import BinaryData
-from .gltf2_blender_animation_utils import simulate_stash, restore_last_action
+from .gltf2_blender_animation_utils import simulate_stash
class BlenderNodeAnim():
@@ -41,62 +41,31 @@ class BlenderNodeAnim():
kf.interpolation = 'LINEAR'
@staticmethod
- def stash_action(gltf, anim_idx, node_idx, action_name):
- node = gltf.data.nodes[node_idx]
- obj = bpy.data.objects[node.blender_object]
-
- if anim_idx not in node.animations.keys():
- return
-
- if (obj.name, action_name) in gltf.actions_stashed.keys():
- return
-
- start_frame = bpy.context.scene.frame_start
-
- animation_name = gltf.data.animations[anim_idx].name
- simulate_stash(obj, animation_name, bpy.data.actions[action_name], start_frame)
-
- gltf.actions_stashed[(obj.name, action_name)] = True
-
- @staticmethod
- def restore_last_action(gltf, node_idx):
- node = gltf.data.nodes[node_idx]
- obj = bpy.data.objects[node.blender_object]
-
- restore_last_action(obj)
-
- @staticmethod
def anim(gltf, anim_idx, node_idx):
"""Manage animation."""
node = gltf.data.nodes[node_idx]
obj = bpy.data.objects[node.blender_object]
fps = bpy.context.scene.render.fps
+ animation = gltf.data.animations[anim_idx]
+
if anim_idx not in node.animations.keys():
return
- animation = gltf.data.animations[anim_idx]
-
- if animation.name:
- name = animation.name + "_" + obj.name
+ for channel_idx in node.animations[anim_idx]:
+ channel = animation.channels[channel_idx]
+ if channel.target.path in ['translation', 'rotation', 'scale']:
+ break
else:
- name = "Animation_" + str(anim_idx) + "_" + obj.name
- if len(name) >= 63:
- # Name is too long to be kept, we are going to keep only animation name for now
- name = animation.name
- if len(name) >= 63:
- # Very long name!
- name = "Animation_" + str(anim_idx)
+ return
+
+ name = animation.track_name + "_" + obj.name
action = bpy.data.actions.new(name)
- # Check if this action has some users.
- # If no user (only 1 indeed), that means that this action must be deleted
- # (is an action from a deleted object)
- if action.users == 1:
- bpy.data.actions.remove(action)
- action = bpy.data.actions.new(name)
+ gltf.needs_stash.append((obj, animation.track_name, action))
+
if not obj.animation_data:
obj.animation_data_create()
- obj.animation_data.action = bpy.data.actions[action.name]
+ obj.animation_data.action = action
for channel_idx in node.animations[anim_idx]:
channel = animation.channels[channel_idx]
@@ -104,92 +73,64 @@ class BlenderNodeAnim():
keys = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].input)
values = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].output)
- if channel.target.path in ['translation', 'rotation', 'scale']:
-
- # There is an animation on object
- # We can't remove Yup2Zup object
- gltf.animation_object = True
-
- if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
- # TODO manage tangent?
- values = [values[idx * 3 + 1] for idx in range(0, len(keys))]
-
- if channel.target.path == "translation":
- blender_path = "location"
- group_name = "Location"
- num_components = 3
- values = [loc_gltf_to_blender(vals) for vals in values]
-
- elif channel.target.path == "rotation":
- blender_path = "rotation_quaternion"
- group_name = "Rotation"
- num_components = 4
- if node.correction_needed is True:
- values = [
- (quaternion_gltf_to_blender(vals).to_matrix().to_4x4() @ correction_rotation()).to_quaternion()
- for vals in values
- ]
- else:
- values = [quaternion_gltf_to_blender(vals) for vals in values]
-
-
- # Manage antipodal quaternions
- for i in range(1, len(values)):
- if values[i].dot(values[i-1]) < 0:
- values[i] = -values[i]
-
- elif channel.target.path == "scale":
- blender_path = "scale"
- group_name = "Scale"
- num_components = 3
- values = [scale_gltf_to_blender(vals) for vals in values]
-
- coords = [0] * (2 * len(keys))
- coords[::2] = (key[0] * fps for key in keys)
-
- if group_name not in action.groups:
- action.groups.new(group_name)
- group = action.groups[group_name]
-
- for i in range(0, num_components):
- fcurve = action.fcurves.new(data_path=blender_path, index=i)
- fcurve.group = group
-
- fcurve.keyframe_points.add(len(keys))
- coords[1::2] = (vals[i] for vals in values)
- fcurve.keyframe_points.foreach_set('co', coords)
-
- # Setting interpolation
- for kf in fcurve.keyframe_points:
- BlenderNodeAnim.set_interpolation(animation.samplers[channel.sampler].interpolation, kf)
- fcurve.update() # force updating tangents (this may change when tangent will be managed)
-
- elif channel.target.path == 'weights':
-
- # retrieve number of targets
- nb_targets = 0
- for prim in gltf.data.meshes[gltf.data.nodes[node_idx].mesh].primitives:
- if prim.targets:
- if len(prim.targets) > nb_targets:
- nb_targets = len(prim.targets)
-
- if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
- factor = 3
- delta = nb_targets
+ if channel.target.path not in ['translation', 'rotation', 'scale']:
+ continue
+
+ # There is an animation on object
+ # We can't remove Yup2Zup object
+ gltf.animation_object = True
+
+ if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
+ # TODO manage tangent?
+ values = [values[idx * 3 + 1] for idx in range(0, len(keys))]
+
+ if channel.target.path == "translation":
+ blender_path = "location"
+ group_name = "Location"
+ num_components = 3
+ values = [loc_gltf_to_blender(vals) for vals in values]
+
+ elif channel.target.path == "rotation":
+ blender_path = "rotation_quaternion"
+ group_name = "Rotation"
+ num_components = 4
+ if node.correction_needed is True:
+ values = [
+ (quaternion_gltf_to_blender(vals).to_matrix().to_4x4() @ correction_rotation()).to_quaternion()
+ for vals in values
+ ]
else:
- factor = 1
- delta = 0
-
- for idx, key in enumerate(keys):
- for sk in range(nb_targets):
- if gltf.shapekeys[sk] is not None: # Do not animate shapekeys not created
- obj.data.shape_keys.key_blocks[gltf.shapekeys[sk]].value = values[factor * idx * nb_targets + delta + sk][0]
- obj.data.shape_keys.key_blocks[gltf.shapekeys[sk]].keyframe_insert(
- "value",
- frame=key[0] * fps,
- group='ShapeKeys'
- )
-
- if action.name not in gltf.current_animation_names.keys():
- gltf.current_animation_names[name] = action.name
+ values = [quaternion_gltf_to_blender(vals) for vals in values]
+
+
+ # Manage antipodal quaternions
+ for i in range(1, len(values)):
+ if values[i].dot(values[i-1]) < 0:
+ values[i] = -values[i]
+
+ elif channel.target.path == "scale":
+ blender_path = "scale"
+ group_name = "Scale"
+ num_components = 3
+ values = [scale_gltf_to_blender(vals) for vals in values]
+
+ coords = [0] * (2 * len(keys))
+ coords[::2] = (key[0] * fps for key in keys)
+
+ if group_name not in action.groups:
+ action.groups.new(group_name)
+ group = action.groups[group_name]
+
+ for i in range(0, num_components):
+ fcurve = action.fcurves.new(data_path=blender_path, index=i)
+ fcurve.group = group
+
+ fcurve.keyframe_points.add(len(keys))
+ coords[1::2] = (vals[i] for vals in values)
+ fcurve.keyframe_points.foreach_set('co', coords)
+
+ # Setting interpolation
+ for kf in fcurve.keyframe_points:
+ BlenderNodeAnim.set_interpolation(animation.samplers[channel.sampler].interpolation, kf)
+ fcurve.update() # force updating tangents (this may change when tangent will be managed)
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_animation_utils.py b/io_scene_gltf2/blender/imp/gltf2_blender_animation_utils.py
index 888d88a4..c835ceb7 100644
--- a/io_scene_gltf2/blender/imp/gltf2_blender_animation_utils.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation_utils.py
@@ -12,7 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-def simulate_stash(obj, gltf_animation_name, action, start_frame):
+import bpy
+
+def simulate_stash(obj, track_name, action, start_frame=None):
# Simulate stash :
# * add a track
# * add an action on track
@@ -20,20 +22,26 @@ def simulate_stash(obj, gltf_animation_name, action, start_frame):
# * remove active action from object
tracks = obj.animation_data.nla_tracks
new_track = tracks.new(prev=None)
- new_track.name = gltf_animation_name if gltf_animation_name is not None else action.name
+ new_track.name = track_name
+ if start_frame is None:
+ start_frame = bpy.context.scene.frame_start
strip = new_track.strips.new(action.name, start_frame, action)
new_track.lock = True
new_track.mute = True
obj.animation_data.action = None
-def restore_last_action(obj):
-
- if not obj.animation_data:
- return
- tracks = obj.animation_data.nla_tracks
- if len(tracks) == 0:
+def restore_animation_on_object(obj, anim_name):
+ if not getattr(obj, 'animation_data', None):
return
- if len(tracks[0].strips) == 0:
+
+ for track in obj.animation_data.nla_tracks:
+ if track.name != anim_name:
+ continue
+ if not track.strips:
+ continue
+
+ obj.animation_data.action = track.strips[0].action
return
- obj.animation_data.action = tracks[0].strips[0].action
+
+ obj.animation_data.action = None
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_animation_weight.py b/io_scene_gltf2/blender/imp/gltf2_blender_animation_weight.py
new file mode 100644
index 00000000..baa0d95e
--- /dev/null
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_animation_weight.py
@@ -0,0 +1,109 @@
+# Copyright 2018-2019 The glTF-Blender-IO authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import bpy
+
+from ...io.imp.gltf2_io_binary import BinaryData
+from .gltf2_blender_animation_utils import simulate_stash
+
+
+class BlenderWeightAnim():
+ """Blender ShapeKey Animation."""
+ def __new__(cls, *args, **kwargs):
+ raise RuntimeError("%s should not be instantiated" % cls)
+
+ @staticmethod
+ def set_interpolation(interpolation, kf):
+ """Manage interpolation."""
+ if interpolation == "LINEAR":
+ kf.interpolation = 'LINEAR'
+ elif interpolation == "STEP":
+ kf.interpolation = 'CONSTANT'
+ elif interpolation == "CUBICSPLINE":
+ kf.interpolation = 'BEZIER'
+ kf.handle_right_type = 'AUTO'
+ kf.handle_left_type = 'AUTO'
+ else:
+ kf.interpolation = 'LINEAR'
+
+ @staticmethod
+ def anim(gltf, anim_idx, node_idx):
+ """Manage animation."""
+ node = gltf.data.nodes[node_idx]
+ obj = bpy.data.objects[node.blender_object]
+ fps = bpy.context.scene.render.fps
+
+ animation = gltf.data.animations[anim_idx]
+
+ if anim_idx not in node.animations.keys():
+ return
+
+ for channel_idx in node.animations[anim_idx]:
+ channel = animation.channels[channel_idx]
+ if channel.target.path == "weights":
+ break
+ else:
+ return
+
+ name = animation.track_name + "_" + obj.name
+ action = bpy.data.actions.new(name)
+ action.id_root = "KEY"
+ gltf.needs_stash.append((obj.data.shape_keys, animation.track_name, action))
+
+ if not obj.data.shape_keys.animation_data:
+ obj.data.shape_keys.animation_data_create()
+ obj.data.shape_keys.animation_data.action = action
+
+ keys = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].input)
+ values = BinaryData.get_data_from_accessor(gltf, animation.samplers[channel.sampler].output)
+
+ # retrieve number of targets
+ nb_targets = 0
+ for prim in gltf.data.meshes[gltf.data.nodes[node_idx].mesh].primitives:
+ if prim.targets:
+ if len(prim.targets) > nb_targets:
+ nb_targets = len(prim.targets)
+
+ if animation.samplers[channel.sampler].interpolation == "CUBICSPLINE":
+ offset = nb_targets
+ stride = 3 * nb_targets
+ else:
+ offset = 0
+ stride = nb_targets
+
+ coords = [0] * (2 * len(keys))
+ coords[::2] = (key[0] * fps for key in keys)
+
+ group_name = "ShapeKeys"
+ if group_name not in action.groups:
+ action.groups.new(group_name)
+ group = action.groups[group_name]
+
+ for sk in range(nb_targets):
+ if gltf.shapekeys[sk] is not None: # Do not animate shapekeys not created
+ kb_name = obj.data.shape_keys.key_blocks[gltf.shapekeys[sk]].name
+ data_path = "key_blocks[" + json.dumps(kb_name) + "].value"
+ fcurve = action.fcurves.new(data_path=data_path)
+ fcurve.group = group
+
+ fcurve.keyframe_points.add(len(keys))
+ coords[1::2] = (values[offset + stride * i + sk][0] for i in range(len(keys)))
+ fcurve.keyframe_points.foreach_set('co', coords)
+
+ # Setting interpolation
+ for kf in fcurve.keyframe_points:
+ BlenderWeightAnim.set_interpolation(animation.samplers[channel.sampler].interpolation, kf)
+ fcurve.update() # force updating tangents (this may change when tangent will be managed)
+
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
index 53b200b2..0f603bf3 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
@@ -256,7 +256,14 @@ class BlenderGlTF():
for node_idx, node in enumerate(gltf.data.nodes):
node.animations = {}
+ track_names = set()
for anim_idx, anim in enumerate(gltf.data.animations):
+ # Pick pair-wise unique name for each animation to use as a name
+ # for its NLA tracks.
+ desired_name = anim.name or "Anim_%d" % anim_idx
+ anim.track_name = BlenderGlTF.find_unused_name(track_names, desired_name)
+ track_names.add(anim.track_name)
+
for channel_idx, channel in enumerate(anim.channels):
if channel.target.node is None:
continue
@@ -274,3 +281,25 @@ class BlenderGlTF():
mesh.blender_name = None
mesh.is_weight_animated = False
+ @staticmethod
+ def find_unused_name(haystack, desired_name):
+ """Finds a name not in haystack and <= 63 UTF-8 bytes.
+ (the limit on the size of a Blender name.)
+ If a is taken, tries a.001, then a.002, etc.
+ """
+ stem = desired_name[:63]
+ suffix = ''
+ cntr = 1
+ while True:
+ name = stem + suffix
+
+ if len(name.encode('utf-8')) > 63:
+ stem = stem[:-1]
+ continue
+
+ if name not in haystack:
+ return name
+
+ suffix = '.%03d' % cntr
+ cntr += 1
+
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_scene.py b/io_scene_gltf2/blender/imp/gltf2_blender_scene.py
index 6e84aac5..aa9684c7 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_scene.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_scene.py
@@ -18,6 +18,7 @@ from mathutils import Quaternion
from .gltf2_blender_node import BlenderNode
from .gltf2_blender_skin import BlenderSkin
from .gltf2_blender_animation import BlenderAnimation
+from .gltf2_blender_animation_utils import simulate_stash
class BlenderScene():
@@ -93,19 +94,23 @@ class BlenderScene():
BlenderSkin.create_armature_modifiers(gltf, skin_id)
if gltf.data.animations:
- gltf.animation_managed = []
for anim_idx, anim in enumerate(gltf.data.animations):
- gltf.current_animation_names = {}
- gltf.actions_stashed= {}
+ # Blender armature name -> action all its bones should use
+ gltf.arma_cache = {}
+ # Things we need to stash when we're done.
+ gltf.needs_stash = []
+
if list_nodes is not None:
for node_idx in list_nodes:
BlenderAnimation.anim(gltf, anim_idx, node_idx)
- for an in gltf.current_animation_names.values():
- gltf.animation_managed.append(an)
- for node_idx in list_nodes:
- BlenderAnimation.stash_action(gltf, anim_idx, node_idx, an)
+
+ for (obj, anim_name, action) in gltf.needs_stash:
+ simulate_stash(obj, anim_name, action)
+
+ # Restore first animation
+ anim_name = gltf.data.animations[0].track_name
for node_idx in list_nodes:
- BlenderAnimation.restore_last_action(gltf, node_idx)
+ BlenderAnimation.restore_animation(gltf, node_idx, anim_name)
if bpy.app.debug_value != 100:
# Parent root node to rotation object