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,