| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925 | /** * Copyright(c) Live2D Inc. All rights reserved. * * Use of this source code is governed by the Live2D Open Software license * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html. */using Live2D.Cubism.Core;using System;using System.IO;using Live2D.Cubism.Framework.MouthMovement;using Live2D.Cubism.Framework.Physics;using Live2D.Cubism.Framework.UserData;using Live2D.Cubism.Framework.Pose;using Live2D.Cubism.Framework.Expression;using Live2D.Cubism.Framework.MotionFade;using Live2D.Cubism.Framework.Raycasting;using Live2D.Cubism.Rendering;using Live2D.Cubism.Rendering.Masking;#if UNITY_EDITORusing UnityEditor;#endifusing UnityEngine;namespace Live2D.Cubism.Framework.Json{    /// <summary>    /// Exposes moc3.json asset data.    /// </summary>    [Serializable]    // ReSharper disable once ClassCannotBeInstantiated    public sealed class CubismModel3Json    {        #region Delegates        /// <summary>        /// Handles the loading of assets.        /// </summary>        /// <param name="assetType">The asset type to load.</param>        /// <param name="assetPath">The path to the asset.</param>        /// <returns></returns>        public delegate object LoadAssetAtPathHandler(Type assetType, string assetPath);        /// <summary>        /// Picks a <see cref="Material"/> for a <see cref="CubismDrawable"/>.        /// </summary>        /// <param name="sender">Event source.</param>        /// <param name="drawable">Drawable to pick for.</param>        /// <returns>Picked material.</returns>        public delegate Material MaterialPicker(CubismModel3Json sender, CubismDrawable drawable);        /// <summary>        /// Picks a <see cref="Texture2D"/> for a <see cref="CubismDrawable"/>.        /// </summary>        /// <param name="sender">Event source.</param>        /// <param name="drawable">Drawable to pick for.</param>        /// <returns>Picked texture.</returns>        public delegate Texture2D TexturePicker(CubismModel3Json sender, CubismDrawable drawable);        #endregion        #region Load Methods        /// <summary>        /// Loads a model.json asset.        /// </summary>        /// <param name="assetPath">The path to the asset.</param>        /// <returns>The <see cref="CubismModel3Json"/> on success; <see langword="null"/> otherwise.</returns>        public static CubismModel3Json LoadAtPath(string assetPath)        {            // Use default asset load handler.            return LoadAtPath(assetPath, BuiltinLoadAssetAtPath);        }        /// <summary>        /// Loads a model.json asset.        /// </summary>        /// <param name="assetPath">The path to the asset.</param>        /// <param name="loadAssetAtPath">Handler for loading assets.</param>        /// <returns>The <see cref="CubismModel3Json"/> on success; <see langword="null"/> otherwise.</returns>        public static CubismModel3Json LoadAtPath(string assetPath, LoadAssetAtPathHandler loadAssetAtPath)        {            // Load Json asset.            var modelJsonAsset = loadAssetAtPath(typeof(string), assetPath) as string;            // Return early in case Json asset wasn't loaded.            if (modelJsonAsset == null)            {                return null;            }            // Deserialize Json.            var modelJson = JsonUtility.FromJson<CubismModel3Json>(modelJsonAsset);            // Finalize deserialization.            modelJson.AssetPath = assetPath;            modelJson.LoadAssetAtPath = loadAssetAtPath;            // Set motion references.            var value = CubismJsonParser.ParseFromString(modelJsonAsset);            // Return early if there is no references.            if (!value.Get("FileReferences").GetMap(null).ContainsKey("Motions"))            {                return modelJson;            }            var motionGroupNames = value.Get("FileReferences").Get("Motions").KeySet().ToArray();            modelJson.FileReferences.Motions.GroupNames = motionGroupNames;            var motionGroupNamesCount = motionGroupNames.Length;            modelJson.FileReferences.Motions.Motions = new SerializableMotion[motionGroupNamesCount][];            for (var i = 0; i < motionGroupNamesCount; i++)            {                var motionGroup = value.Get("FileReferences").Get("Motions").Get(motionGroupNames[i]);                var motionCount = motionGroup.GetVector(null).ToArray().Length;                modelJson.FileReferences.Motions.Motions[i] = new SerializableMotion[motionCount];                for (var j = 0; j < motionCount; j++)                {                    if (motionGroup.Get(j).GetMap(null).ContainsKey("File"))                    {                        modelJson.FileReferences.Motions.Motions[i][j].File = motionGroup.Get(j).Get("File").toString();                    }                    if (motionGroup.Get(j).GetMap(null).ContainsKey("Sound"))                    {                        modelJson.FileReferences.Motions.Motions[i][j].Sound = motionGroup.Get(j).Get("Sound").toString();                    }                    if (motionGroup.Get(j).GetMap(null).ContainsKey("FadeInTime"))                    {                        modelJson.FileReferences.Motions.Motions[i][j].FadeInTime = motionGroup.Get(j).Get("FadeInTime").ToFloat();                    }                    if (motionGroup.Get(j).GetMap(null).ContainsKey("FadeOutTime"))                    {                        modelJson.FileReferences.Motions.Motions[i][j].FadeOutTime = motionGroup.Get(j).Get("FadeOutTime").ToFloat();                    }                }            }            return modelJson;        }        #endregion        /// <summary>        /// Path to <see langword="this"/>.        /// </summary>        public string AssetPath { get; private set; }        /// <summary>        /// Method for loading assets.        /// </summary>        private LoadAssetAtPathHandler LoadAssetAtPath { get; set; }        #region Json Data        /// <summary>        /// The motion3.json format version.        /// </summary>        [SerializeField]        public int Version;        /// <summary>        /// The file references.        /// </summary>        [SerializeField]        public SerializableFileReferences FileReferences;        /// <summary>        /// Groups.        /// </summary>        [SerializeField]        public SerializableGroup[] Groups;        /// <summary>        /// Hit areas.        /// </summary>        [SerializeField]        public SerializableHitArea[] HitAreas;        #endregion        /// <summary>        /// The contents of the referenced moc3 asset.        /// </summary>        /// <remarks>        /// The contents isn't cached internally.        /// </remarks>        public byte[] Moc3        {            get            {                return LoadReferencedAsset<byte[]>(FileReferences.Moc);            }        }        /// <summary>        /// <see cref="CubismPose3Json"/> backing field.        /// </summary>        [NonSerialized]        private CubismPose3Json _pose3Json;        /// <summary>        /// The contents of pose3.json asset.        /// </summary>        public CubismPose3Json Pose3Json        {            get            {                if(_pose3Json != null)                {                    return _pose3Json;                }                var jsonString = string.IsNullOrEmpty(FileReferences.Pose) ? null : LoadReferencedAsset<String>(FileReferences.Pose);                _pose3Json = CubismPose3Json.LoadFrom(jsonString);                return _pose3Json;            }        }        /// <summary>        /// <see cref="Expression3Jsons"/> backing field.        /// </summary>        [NonSerialized]        private CubismExp3Json[] _expression3Jsons;        /// <summary>        /// The referenced expression assets.        /// </summary>        /// <remarks>        /// The references aren't cached internally.        /// </remarks>        public CubismExp3Json[] Expression3Jsons        {            get            {                // Fail silently...                if(FileReferences.Expressions == null)                {                    return null;                }                // Load expression only if necessary.                if (_expression3Jsons == null)                {                    _expression3Jsons = new CubismExp3Json[FileReferences.Expressions.Length];                    for (var i = 0; i < _expression3Jsons.Length; ++i)                    {                        var expressionJson = (string.IsNullOrEmpty(FileReferences.Expressions[i].File))                                                ? null                                                : LoadReferencedAsset<string>(FileReferences.Expressions[i].File);                        _expression3Jsons[i] = CubismExp3Json.LoadFrom(expressionJson);                    }                }                return _expression3Jsons;            }        }        /// <summary>        /// The contents of physics3.json asset.        /// </summary>        public string Physics3Json        {            get            {                return string.IsNullOrEmpty(FileReferences.Physics) ? null : LoadReferencedAsset<string>(FileReferences.Physics);            }        }        public string UserData3Json        {            get            {                return string.IsNullOrEmpty(FileReferences.UserData) ? null : LoadReferencedAsset<string>(FileReferences.UserData);            }        }        /// <summary>        /// The contents of cdi3.json asset.        /// </summary>        public string DisplayInfo3Json        {            get            {                return string.IsNullOrEmpty(FileReferences.DisplayInfo) ? null : LoadReferencedAsset<string>(FileReferences.DisplayInfo);            }        }        /// <summary>        /// <see cref="Textures"/> backing field.        /// </summary>        [NonSerialized]        private Texture2D[] _textures;        /// <summary>        /// The referenced texture assets.        /// </summary>        /// <remarks>        /// The references aren't cached internally.        /// </remarks>        public Texture2D[] Textures        {            get            {                // Load textures only if necessary.                if (_textures == null)                {                    _textures = new Texture2D[FileReferences.Textures.Length];                    for (var i = 0; i < _textures.Length; ++i)                    {                        _textures[i] = LoadReferencedAsset<Texture2D>(FileReferences.Textures[i]);                    }                }                return _textures;            }        }        #region Constructors        /// <summary>        /// Makes construction only possible through factories.        /// </summary>        private CubismModel3Json()        {        }        #endregion        /// <summary>        /// Instantiates a <see cref="CubismMoc">model source</see> and a <see cref="CubismModel">model</see> with the default texture set.        /// </summary>        /// <param name="shouldImportAsOriginalWorkflow">Should import as original workflow.</param>        /// <returns>The instantiated <see cref="CubismModel">model</see> on success; <see langword="null"/> otherwise.</returns>        public CubismModel ToModel(bool shouldImportAsOriginalWorkflow = false)        {            return ToModel(CubismBuiltinPickers.MaterialPicker, CubismBuiltinPickers.TexturePicker, shouldImportAsOriginalWorkflow);        }        /// <summary>        /// Instantiates a <see cref="CubismMoc">model source</see> and a <see cref="CubismModel">model</see>.        /// </summary>        /// <param name="pickMaterial">The material mapper to use.</param>        /// <param name="pickTexture">The texture mapper to use.</param>        /// <param name="shouldImportAsOriginalWorkflow">Should import as original workflow.</param>        /// <returns>The instantiated <see cref="CubismModel">model</see> on success; <see langword="null"/> otherwise.</returns>        public CubismModel ToModel(MaterialPicker pickMaterial, TexturePicker pickTexture, bool shouldImportAsOriginalWorkflow = false)        {            // Initialize model source and instantiate it.            var mocAsBytes = Moc3;            if (mocAsBytes == null)            {                return null;            }            var moc = CubismMoc.CreateFrom(mocAsBytes);            var model = CubismModel.InstantiateFrom(moc);            model.name = Path.GetFileNameWithoutExtension(FileReferences.Moc);#if UNITY_EDITOR            // Add parameters and parts inspectors.            model.gameObject.AddComponent<CubismParametersInspector>();            model.gameObject.AddComponent<CubismPartsInspector>();#endif            // Create renderers.            var rendererController = model.gameObject.AddComponent<CubismRenderController>();            var renderers = rendererController.Renderers;            var drawables = model.Drawables;            // Initialize materials.            for (var i = 0; i < renderers.Length; ++i)            {                renderers[i].Material = pickMaterial(this, drawables[i]);            }            // Initialize textures.            for (var i = 0; i < renderers.Length; ++i)            {                renderers[i].MainTexture = pickTexture(this, drawables[i]);            }            // Initialize drawables.            if(HitAreas != null)            {                for (var i = 0; i < HitAreas.Length; i++)                {                    for (var j = 0; j < drawables.Length; j++)                    {                        if (drawables[j].Id == HitAreas[i].Id)                        {                            // Add components for hit judgement to HitArea target Drawables.                            var hitDrawable = drawables[j].gameObject.AddComponent<CubismHitDrawable>();                            hitDrawable.Name = HitAreas[i].Name;                            drawables[j].gameObject.AddComponent<CubismRaycastable>();                            break;                        }                    }                }            }            //Load from cdi3.json            var DisplayInfo3JsonAsString = DisplayInfo3Json;            var cdi3Json = CubismDisplayInfo3Json.LoadFrom(DisplayInfo3JsonAsString);            // Initialize groups.            var parameters = model.Parameters;            for (var i = 0; i < parameters.Length; ++i)            {                if (IsParameterInGroup(parameters[i], "EyeBlink"))                {                    if (model.gameObject.GetComponent<CubismEyeBlinkController>() == null)                    {                        model.gameObject.AddComponent<CubismEyeBlinkController>();                    }                    parameters[i].gameObject.AddComponent<CubismEyeBlinkParameter>();                }                // Set up mouth parameters.                if (IsParameterInGroup(parameters[i], "LipSync"))                {                    if (model.gameObject.GetComponent<CubismMouthController>() == null)                    {                        model.gameObject.AddComponent<CubismMouthController>();                    }                    parameters[i].gameObject.AddComponent<CubismMouthParameter>();                }                // Setting up the parameter name for display.                if (cdi3Json != null)                {                    var cubismDisplayInfoParameterName = parameters[i].gameObject.AddComponent<CubismDisplayInfoParameterName>();                    cubismDisplayInfoParameterName.Name = cdi3Json.Parameters[i].Name;                    cubismDisplayInfoParameterName.DisplayName = string.Empty;                }            }            // Setting up the part name for display.            if (cdi3Json != null)            {                // Initialize groups.                var parts = model.Parts;                for (var i = 0; i < parts.Length; i++)                {                    var cubismDisplayInfoPartNames = parts[i].gameObject.AddComponent<CubismDisplayInfoPartName>();                    cubismDisplayInfoPartNames.Name = cdi3Json.Parts[i].Name;                    cubismDisplayInfoPartNames.DisplayName = string.Empty;                }            }            // Add mask controller if required.            for (var i = 0; i < drawables.Length; ++i)            {                if (!drawables[i].IsMasked)                {                    continue;                }                // Add controller exactly once...                model.gameObject.AddComponent<CubismMaskController>();                break;            }            // Add original workflow component if is original workflow.            if(shouldImportAsOriginalWorkflow)            {                // Add cubism update manager.                var updateManager = model.gameObject.GetComponent<CubismUpdateController>();                if(updateManager == null)                {                    model.gameObject.AddComponent<CubismUpdateController>();                }                // Add parameter store.                var parameterStore = model.gameObject.GetComponent<CubismParameterStore>();                if(parameterStore == null)                {                    parameterStore = model.gameObject.AddComponent<CubismParameterStore>();                }                // Add pose controller.                var poseController = model.gameObject.GetComponent<CubismPoseController>();                if(poseController == null)                {                    poseController = model.gameObject.AddComponent<CubismPoseController>();                }                // Add expression controller.                var expressionController = model.gameObject.GetComponent<CubismExpressionController>();                if(expressionController == null)                {                    expressionController = model.gameObject.AddComponent<CubismExpressionController>();                }                // Add fade controller.                var motionFadeController = model.gameObject.GetComponent<CubismFadeController>();                if(motionFadeController == null)                {                    motionFadeController = model.gameObject.AddComponent<CubismFadeController>();                }            }            // Initialize physics if JSON exists.            var physics3JsonAsString = Physics3Json;            if (!string.IsNullOrEmpty(physics3JsonAsString))            {                var physics3Json = CubismPhysics3Json.LoadFrom(physics3JsonAsString);                var physicsController = model.gameObject.GetComponent<CubismPhysicsController>();                if (physicsController == null)                {                    physicsController = model.gameObject.AddComponent<CubismPhysicsController>();                }                physicsController.Initialize(physics3Json.ToRig());            }            var userData3JsonAsString = UserData3Json;            if (!string.IsNullOrEmpty(userData3JsonAsString))            {                var userData3Json = CubismUserData3Json.LoadFrom(userData3JsonAsString);                var drawableBodies = userData3Json.ToBodyArray(CubismUserDataTargetType.ArtMesh);                for (var i = 0; i < drawables.Length; ++i)                {                    var index = GetBodyIndexById(drawableBodies, drawables[i].Id);                    if (index >= 0)                    {                        var tag = drawables[i].gameObject.GetComponent<CubismUserDataTag>();                        if (tag == null)                        {                            tag = drawables[i].gameObject.AddComponent<CubismUserDataTag>();                        }                        tag.Initialize(drawableBodies[index]);                    }                }            }            if (model.gameObject.GetComponent<Animator>() == null)            {                model.gameObject.AddComponent<Animator>();            }            // Make sure model is 'fresh'            model.ForceUpdateNow();            return model;        }        #region Helper Methods        /// <summary>        /// Type-safely loads an asset.        /// </summary>        /// <typeparam name="T">Asset type.</typeparam>        /// <param name="referencedFile">Path to asset.</param>        /// <returns>The asset on success; <see langword="null"/> otherwise.</returns>        private T LoadReferencedAsset<T>(string referencedFile) where T : class        {            var assetPath = Path.GetDirectoryName(AssetPath) + "/" + referencedFile;            return LoadAssetAtPath(typeof(T), assetPath) as T;        }        /// <summary>        /// Builtin method for loading assets.        /// </summary>        /// <param name="assetType">Asset type.</param>        /// <param name="assetPath">Path to asset.</param>        /// <returns>The asset on success; <see langword="null"/> otherwise.</returns>        private static object BuiltinLoadAssetAtPath(Type assetType, string assetPath)        {            // Explicitly deal with byte arrays.            if (assetType == typeof(byte[]))            {#if UNITY_EDITOR                return File.ReadAllBytes(assetPath);#else                var textAsset = Resources.Load(assetPath, typeof(TextAsset)) as TextAsset;                return (textAsset != null)                    ? textAsset.bytes                    : null;#endif            }            else if (assetType == typeof(string))            {#if UNITY_EDITOR                return File.ReadAllText(assetPath);#else                var textAsset = Resources.Load(assetPath, typeof(TextAsset)) as TextAsset;                return (textAsset != null)                    ? textAsset.text                    : null;#endif            }#if UNITY_EDITOR            return AssetDatabase.LoadAssetAtPath(assetPath, assetType);#else            return Resources.Load(assetPath, assetType);#endif        }        /// <summary>        /// Checks whether the parameter is an eye blink parameter.        /// </summary>        /// <param name="parameter">Parameter to check.</param>        /// <param name="groupName">Name of group to query for.</param>        /// <returns><see langword="true"/> if parameter is an eye blink parameter; <see langword="false"/> otherwise.</returns>        private bool IsParameterInGroup(CubismParameter parameter, string groupName)        {            // Return early if groups aren't available...            if (Groups == null || Groups.Length == 0)            {                return false;            }            for (var i = 0; i < Groups.Length; ++i)            {                if (Groups[i].Name != groupName)                {                    continue;                }                if(Groups[i].Ids != null)                {                    for (var j = 0; j < Groups[i].Ids.Length; ++j)                    {                        if (Groups[i].Ids[j] == parameter.name)                        {                            return true;                        }                    }                }            }            return false;        }        /// <summary>        /// Get body index from body array by Id.        /// </summary>        /// <param name="bodies">Target body array.</param>        /// <param name="id">Id for find.</param>        /// <returns>Array index if Id found; -1 otherwise.</returns>        private int GetBodyIndexById(CubismUserDataBody[] bodies, string id)        {            for (var i = 0; i < bodies.Length; ++i)            {                if (bodies[i].Id == id)                {                    return i;                }            }            return -1;        }        #endregion        #region Json Helpers        /// <summary>        /// File references data.        /// </summary>        [Serializable]        public struct SerializableFileReferences        {            /// <summary>            /// Relative path to the moc3 asset.            /// </summary>            [SerializeField]            public string Moc;            /// <summary>            /// Relative paths to texture assets.            /// </summary>            [SerializeField]            public string[] Textures;            /// <summary>            /// Relative path to the pose3.json.            /// </summary>            [SerializeField]            public string Pose;            /// <summary>            /// Relative path to the expression asset.            /// </summary>            [SerializeField]            public SerializableExpression[] Expressions;            /// <summary>            /// Relative path to the pose motion3.json.            /// </summary>            [SerializeField]            public SerializableMotions Motions;            /// <summary>            /// Relative path to the physics asset.            /// </summary>            [SerializeField]            public string Physics;            /// <summary>            /// Relative path to the user data asset.            /// </summary>            [SerializeField]            public string UserData;            /// <summary>            /// Relative path to the cdi3.json.            /// </summary>            [SerializeField]            public string DisplayInfo;        }        /// <summary>        /// Group data.        /// </summary>        [Serializable]        public struct SerializableGroup        {            /// <summary>            /// Target type.            /// </summary>            [SerializeField]            public string Target;            /// <summary>            /// Group name.            /// </summary>            [SerializeField]            public string Name;            /// <summary>            /// Referenced IDs.            /// </summary>            [SerializeField]            public string[] Ids;        }        /// <summary>        /// Expression data.        /// </summary>        [Serializable]        public struct SerializableExpression        {            /// <summary>            /// Expression Name.            /// </summary>            [SerializeField]            public string Name;            /// <summary>            /// Expression File.            /// </summary>            [SerializeField]            public string File;            /// <summary>            /// Expression FadeInTime.            /// </summary>            [SerializeField]            public float FadeInTime;            /// <summary>            /// Expression FadeOutTime.            /// </summary>            [SerializeField]            public float FadeOutTime;        }        /// <summary>        /// Motion data.        /// </summary>        [Serializable]        public struct SerializableMotions        {            /// <summary>            /// Motion group names.            /// </summary>            [SerializeField]            public string[] GroupNames;            /// <summary>            /// Motion groups.            /// </summary>            [SerializeField]            public SerializableMotion[][] Motions;        }        /// <summary>        /// Motion data.        /// </summary>        [Serializable]        public struct SerializableMotion        {            /// <summary>            /// File path.            /// </summary>            [SerializeField]            public string File;            /// <summary>            /// Sound path.            /// </summary>            [SerializeField]            public string Sound;            /// <summary>            /// Fade in time.            /// </summary>            [SerializeField]            public float FadeInTime;            /// <summary>            /// Fade out time.            /// </summary>            [SerializeField]            public float FadeOutTime;        }        /// <summary>        /// Hit Area.        /// </summary>        [Serializable]        public struct SerializableHitArea        {            /// <summary>            /// Hit area name.            /// </summary>            [SerializeField]            public string Name;            /// <summary>            /// Hit area id.            /// </summary>            [SerializeField]            public string Id;        }        #endregion    }}
 |