Repository: khronosgroup/gltf-blender-io
Issue: Animations of shuffled Shape keys doesn't work as expected when exported as GLB/GLTF

**Description:**
In general, Blender obeys the order in which shape keys are applied. By default, they are named "Basis' (which records the initial position of the vertices), 'Key 1'. 'Key 2', etc. 
We have the option to interchange this order, even putting 'Key n' (n is an integer) at the top in the position of 'Basis'. 
Let's create one shape key in addition to Basis and it's named 'Key 1'. By default, its 'Relative to' value is set to 'Basis'. For some reason, let's say we need to change the order, so we shift 'Key 1' to the top and 'Basis' to the bottom and changed the 'Relative to' option of 'Basis' to 'Key 1'.
Now we know that we didn't change the 'Relative to' option for 'Key 1' before shifting and it's still set to 'Basis'. But Blender disables those options for the Top most shape key. 
We animate the 'Basis' shape key and it works as expected within Blender since it respects the order no matter what 'Relative to' 
value it had before shifting to the top.
When exported as GLB/GLTF, the 'Basis' shape key is animated, but the mesh doesn't morph from 'Key 1' to 'Basis' (Test done in https://gltf-viewer.donmccurdy.com/).

**Steps to reproduce the behavior:**
1. Create a shape key 'Key 1' and edit the mesh while this shape key is selected.
![image](https://github.com/KhronosGroup/glTF-Blender-IO/assets/106798081/1df2f7f4-b662-4a8f-b767-d5fd0cef1bb3)
![image](https://github.com/KhronosGroup/glTF-Blender-IO/assets/106798081/2b3df9b4-cc97-40e4-a6ba-2538a915bb39)

2. Shift 'Key 1' to the top and change the 'Relative to' option of 'Basis' to 'Key 1'
![image](https://github.com/KhronosGroup/glTF-Blender-IO/assets/106798081/b34e2b40-2683-4aac-b89a-db6d97e4fa1a)

3. Animate the 'Basis' shape key from 0 to 1.
![ezgif com-optimize](https://github.com/KhronosGroup/glTF-Blender-IO/assets/106798081/e29c2d6f-ae2a-4473-a3c6-e860b537fc6b)
It works as expected in Blender.
4. Now export the file as GLB/GLTF. I mostly left the export option as default values except for "include -> selected objects"
![image](https://github.com/KhronosGroup/glTF-Blender-IO/assets/106798081/c3b090f6-001f-4436-b3f1-562a1c40d840)
but it doesn't animate as expected
![ezgif com-video-to-gif](https://github.com/KhronosGroup/glTF-Blender-IO/assets/106798081/4346fc60-94e5-4e79-8d11-03532cadb619)

**Expected behavior**
The GLB/GLTF is expected to animate shape keys as it's done in Blender.

**.blend and .glb files**
[GLB_shapekey_export_issue.zip](https://github.com/KhronosGroup/glTF-Blender-IO/files/12238192/GLB_shapekey_export_issue.zip)

**Version**
 - OS: Windows 11
 - Blender Version 3.6.1 - stable

**Current workaround**
I've found a workaround to avoid this issue. 
Before shifting any shape key to the top, change it's Relative to' option to itself, as shown below,
![image](https://github.com/KhronosGroup/glTF-Blender-IO/assets/106798081/3ec7d37f-bc25-4ace-8c9a-8c9dc55668e5)

**Conclusion:**
It'd be helpful for the users if the developers could adjust the shape key values for GLB/GLTF in a way Blender does it internally.

Thank you.

Changed files:
addons/io_scene_gltf2/blender/com/gltf2_blender_data_path.py
addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_channels.py
addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_keyframes.py
addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_drivers.py

diff --git a/addons/io_scene_gltf2/blender/com/gltf2_blender_data_path.py b/addons/io_scene_gltf2/blender/com/gltf2_blender_data_path.py
index 5cef76ab..53457321 100644
--- a/addons/io_scene_gltf2/blender/com/gltf2_blender_data_path.py
+++ b/addons/io_scene_gltf2/blender/com/gltf2_blender_data_path.py
@@ -55,10 +55,19 @@ def is_bone_anim_channel(data_path: str) -> bool:
 
 def get_sk_exported(key_blocks):
     return [
-            key_block
-            for key_block in key_blocks
-            if not skip_sk(key_block)
+            k
+            for k in key_blocks
+            if not skip_sk(key_blocks, k)
         ]
 
-def skip_sk(k):
-    return k == k.relative_key or k.mute
+def skip_sk(key_blocks, k):
+    # Do not export:
+     # - if muted
+     # - if relative key is SK itself (this avoid exporting Basis too if user didn't change order)
+     # - the Basis (the first SK of the list)
+    return k == k.relative_key \\
+        or k.mute \\
+        or is_first_index(key_blocks, k) is True
+
+def is_first_index(key_blocks, k):
+    return key_blocks[0].name == k.name
diff --git a/addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_channels.py b/addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_channels.py
index 6d6baa87..7ad21645 100644
--- a/addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_channels.py
+++ b/addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_channels.py
@@ -86,7 +86,7 @@ def get_channel_groups(obj_uuid: str, blender_action: bpy.types.Action, export_s
                 type_ = "BONE"
                 if blender_object.type == "MESH" and object_path.startswith("key_blocks"):
                     shape_key = blender_object.data.shape_keys.path_resolve(object_path)
-                    if skip_sk(shape_key):
+                    if skip_sk(blender_object.data.shape_keys.key_blocks, shape_key):
                         continue
                     target = blender_object.data.shape_keys
                     type_ = "SK"
@@ -96,7 +96,7 @@ def get_channel_groups(obj_uuid: str, blender_action: bpy.types.Action, export_s
                 if blender_object.type == "MESH":
                     try:
                         shape_key = blender_object.data.shape_keys.path_resolve(object_path)
-                        if skip_sk(shape_key):
+                        if skip_sk(blender_object.data.shape_keys.key_blocks, shape_key):
                             continue
                         target = blender_object.data.shape_keys
                         type_ = "SK"
@@ -191,7 +191,7 @@ def __get_channel_group_sorted(channels: typing.Tuple[bpy.types.FCurve], blender
             shapekeys_idx = {}
             cpt_sk = 0
             for sk in blender_object.data.shape_keys.key_blocks:
-                if skip_sk(sk):
+                if skip_sk(blender_object.data.shape_keys.key_blocks, sk):
                     continue
                 shapekeys_idx[sk.name] = cpt_sk
                 cpt_sk += 1
diff --git a/addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_keyframes.py b/addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_keyframes.py
index bec9835f..731eb74a 100644
--- a/addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_keyframes.py
+++ b/addons/io_scene_gltf2/blender/exp/animation/fcurves/gltf2_blender_gather_fcurves_keyframes.py
@@ -14,7 +14,7 @@
 
 import bpy
 import typing
-from .....blender.com.gltf2_blender_data_path import skip_sk
+from .....blender.com.gltf2_blender_data_path import get_sk_exported
 from ....com.gltf2_blender_data_path import get_target_object_path
 from ...gltf2_blender_gather_cache import cached
 from ..gltf2_blender_gather_keyframes import Keyframe
@@ -175,9 +175,7 @@ def __gather_non_keyed_values(
         if object_path:
             shapekeys_idx = {}
             cpt_sk = 0
-            for sk in blender_object.data.shape_keys.key_blocks:
-                if skip_sk(sk):
-                    continue
+            for sk in get_sk_exported(blender_object.data.shape_keys.key_blocks):
                 shapekeys_idx[cpt_sk] = sk.name
                 cpt_sk += 1
 
diff --git a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_drivers.py b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_drivers.py
index ae7ade14..5b11d5c4 100644
--- a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_drivers.py
+++ b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_drivers.py
@@ -71,7 +71,7 @@ def get_sk_drivers(blender_armature_uuid, export_settings):
                 sk_name = child.data.shape_keys.path_resolve(get_target_object_path(sk_c.data_path)).name
             except:
                 continue
-            if skip_sk(child.data.shape_keys.key_blocks[sk_name]):
+            if skip_sk(child.data.shape_keys.key_blocks, child.data.shape_keys.key_blocks[sk_name]):
                 continue
             idx_channel_mapping.append((shapekeys_idx[sk_name], sk_c))
         existing_idx = dict(idx_channel_mapping)