diff --git a/Assets/VRM10/Runtime/Components/VRM10Object/VRM10ObjectExpression.cs b/Assets/VRM10/Runtime/Components/VRM10Object/VRM10ObjectExpression.cs
index e9a99e213..ca4b8bc99 100644
--- a/Assets/VRM10/Runtime/Components/VRM10Object/VRM10ObjectExpression.cs
+++ b/Assets/VRM10/Runtime/Components/VRM10Object/VRM10ObjectExpression.cs
@@ -9,6 +9,8 @@ namespace UniVRM10
[Serializable]
public sealed class VRM10ObjectExpression
{
+ public const string NEUTRAL_KEY = "Neutral";
+
#region Preset
[SerializeField, Header("Emotion")]
public VRM10Expression Happy;
diff --git a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs
index 0468fd08a..82dcc4a3b 100644
--- a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs
+++ b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs
@@ -427,6 +427,14 @@ namespace UniVRM10
{
var key = ExpressionKey.CreateCustom(name);
var preset = ExpressionPreset.custom;
+ if (preset == ExpressionPreset.custom && name == VRM10ObjectExpression.NEUTRAL_KEY)
+ {
+ // TODO:
+ // 注意!JsonSchema の更新待ち?
+ key = ExpressionKey.CreateFromPreset(ExpressionPreset.neutral);
+ Debug.Log($"set neutral");
+ preset = ExpressionPreset.neutral;
+ }
var clip = GetOrLoadExpression(key.SubAssetKey, preset, expression);
if (clip != null)
{
diff --git a/Assets/VRM10/Runtime/Migration/MigrationVrm.cs b/Assets/VRM10/Runtime/Migration/MigrationVrm.cs
index e0039714f..294ac93dd 100644
--- a/Assets/VRM10/Runtime/Migration/MigrationVrm.cs
+++ b/Assets/VRM10/Runtime/Migration/MigrationVrm.cs
@@ -40,6 +40,20 @@ namespace UniVRM10
return MigrateVrm(gltf, bin, data.Json.ParseAsJson()["extensions"]["VRM"]);
}
+ ///
+ /// dst が null の場合だけ代入する。
+ /// 先に来た方を有効にしたい。
+ ///
+ ///
+ ///
+ static void SetIfNull(ref UniGLTF.Extensions.VRMC_vrm.Expression dst, UniGLTF.Extensions.VRMC_vrm.Expression src)
+ {
+ if (dst == null)
+ {
+ dst = src;
+ }
+ }
+
static byte[] MigrateVrm(glTF gltf, ArraySegment bin, JsonNode vrm0)
{
var meshToNode = CreateMeshToNode(gltf);
@@ -65,25 +79,38 @@ namespace UniVRM10
{
switch (preset)
{
- case ExpressionPreset.happy: vrm1.Expressions.Preset.Happy = expression; break;
- case ExpressionPreset.angry: vrm1.Expressions.Preset.Angry = expression; break;
- case ExpressionPreset.sad: vrm1.Expressions.Preset.Sad = expression; break;
- case ExpressionPreset.relaxed: vrm1.Expressions.Preset.Relaxed = expression; break;
- case ExpressionPreset.surprised: vrm1.Expressions.Preset.Surprised = expression; break;
- case ExpressionPreset.aa: vrm1.Expressions.Preset.Aa = expression; break;
- case ExpressionPreset.ih: vrm1.Expressions.Preset.Ih = expression; break;
- case ExpressionPreset.ou: vrm1.Expressions.Preset.Ou = expression; break;
- case ExpressionPreset.ee: vrm1.Expressions.Preset.Ee = expression; break;
- case ExpressionPreset.oh: vrm1.Expressions.Preset.Oh = expression; break;
- case ExpressionPreset.blink: vrm1.Expressions.Preset.Blink = expression; break;
- case ExpressionPreset.blinkLeft: vrm1.Expressions.Preset.BlinkLeft = expression; break;
- case ExpressionPreset.blinkRight: vrm1.Expressions.Preset.BlinkRight = expression; break;
- case ExpressionPreset.lookUp: vrm1.Expressions.Preset.LookUp = expression; break;
- case ExpressionPreset.lookDown: vrm1.Expressions.Preset.LookDown = expression; break;
- case ExpressionPreset.lookLeft: vrm1.Expressions.Preset.LookLeft = expression; break;
- case ExpressionPreset.lookRight: vrm1.Expressions.Preset.LookRight = expression; break;
- case ExpressionPreset.neutral: vrm1.Expressions.Custom[customName] = expression; break;
- case ExpressionPreset.custom: vrm1.Expressions.Custom[customName] = expression; break;
+ case ExpressionPreset.happy: SetIfNull(ref vrm1.Expressions.Preset.Happy, expression); break;
+ case ExpressionPreset.angry: SetIfNull(ref vrm1.Expressions.Preset.Angry, expression); break;
+ case ExpressionPreset.sad: SetIfNull(ref vrm1.Expressions.Preset.Sad, expression); break;
+ case ExpressionPreset.relaxed: SetIfNull(ref vrm1.Expressions.Preset.Relaxed, expression); break;
+ case ExpressionPreset.surprised: SetIfNull(ref vrm1.Expressions.Preset.Surprised, expression); break;
+ case ExpressionPreset.aa: SetIfNull(ref vrm1.Expressions.Preset.Aa, expression); break;
+ case ExpressionPreset.ih: SetIfNull(ref vrm1.Expressions.Preset.Ih, expression); break;
+ case ExpressionPreset.ou: SetIfNull(ref vrm1.Expressions.Preset.Ou, expression); break;
+ case ExpressionPreset.ee: SetIfNull(ref vrm1.Expressions.Preset.Ee, expression); break;
+ case ExpressionPreset.oh: SetIfNull(ref vrm1.Expressions.Preset.Oh, expression); break;
+ case ExpressionPreset.blink: SetIfNull(ref vrm1.Expressions.Preset.Blink, expression); break;
+ case ExpressionPreset.blinkLeft: SetIfNull(ref vrm1.Expressions.Preset.BlinkLeft, expression); break;
+ case ExpressionPreset.blinkRight: SetIfNull(ref vrm1.Expressions.Preset.BlinkRight, expression); break;
+ case ExpressionPreset.lookUp: SetIfNull(ref vrm1.Expressions.Preset.LookUp, expression); break;
+ case ExpressionPreset.lookDown: SetIfNull(ref vrm1.Expressions.Preset.LookDown, expression); break;
+ case ExpressionPreset.lookLeft: SetIfNull(ref vrm1.Expressions.Preset.LookLeft, expression); break;
+ case ExpressionPreset.lookRight: SetIfNull(ref vrm1.Expressions.Preset.LookRight, expression); break;
+ case ExpressionPreset.neutral:
+ // TODO: 仕様確定待ち
+ // Presetに格上げするか、小文字にする
+ vrm1.Expressions.Custom[VRM10ObjectExpression.NEUTRAL_KEY] = expression;
+ break;
+ case ExpressionPreset.custom:
+ if (vrm1.Expressions.Custom.ContainsKey(customName))
+ {
+ // 同名が既存。不採用
+ }
+ else
+ {
+ vrm1.Expressions.Custom[customName] = expression;
+ }
+ break;
default: throw new NotImplementedException();
}
}
diff --git a/Assets/VRM10/Runtime/Migration/MigrationVrmExpression.cs b/Assets/VRM10/Runtime/Migration/MigrationVrmExpression.cs
index b0eb44603..628863a98 100644
--- a/Assets/VRM10/Runtime/Migration/MigrationVrmExpression.cs
+++ b/Assets/VRM10/Runtime/Migration/MigrationVrmExpression.cs
@@ -8,12 +8,29 @@ namespace UniVRM10
{
public static class MigrationVrmExpression
{
- static ExpressionPreset ToPreset(JsonNode json)
+ ///
+ /// preset 名の文字列から ExpressionPreset を確定させる。
+ ///
+ /// 0.x の特殊な挙動として、
+ /// preset名が "unknown" の場合に、
+ /// "name" を preset 名として解釈を試みる。
+ ///
+ ///
+ ///
+ ///
+ static ExpressionPreset ToPreset(JsonNode json, string name)
{
- switch (json.GetString().ToLower())
+ var src = json.GetString().ToLowerInvariant();
+ if (src == "unknown")
{
- case "unknown": return ExpressionPreset.custom;
+ // VRM 0.x では以下の実装が存在した。
+ // Preset が unknown で Name が Joy の表情があったときに
+ // それは Preset が Joy であるとみなしてロードしていた。
+ src = name.ToLowerInvariant();
+ }
+ switch (src)
+ {
// https://github.com/vrm-c/vrm-specification/issues/185
case "neutral": return ExpressionPreset.neutral;
@@ -39,7 +56,7 @@ namespace UniVRM10
case "lookright": return ExpressionPreset.lookRight;
}
- throw new NotImplementedException();
+ return ExpressionPreset.custom;
}
static IEnumerable ToMorphTargetBinds(JsonNode json,
@@ -199,7 +216,7 @@ namespace UniVRM10
{
isBinary = isBinaryNode.GetBoolean();
}
- var preset = ToPreset(blendShapeClip["presetName"]);
+ var preset = ToPreset(blendShapeClip["presetName"], name);
var expression = new UniGLTF.Extensions.VRMC_vrm.Expression
{
IsBinary = isBinary,