1052 lines
34 KiB
C#
1052 lines
34 KiB
C#
/**
|
|
* 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;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
using UnityEngine;
|
|
|
|
|
|
namespace Live2D.Cubism.Framework.Json
|
|
{
|
|
/// <summary>
|
|
/// Exposes moc3.json asset data.
|
|
/// </summary>
|
|
[Serializable]
|
|
// ReSharper disable once ClassCannotBeInstantiated
|
|
public sealed class CubismModel3Json
|
|
{
|
|
public static readonly string ModelCanvasName = "ModelCanvas";
|
|
|
|
#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 DrawableMaterialPicker(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);
|
|
|
|
/// <summary>
|
|
/// Picks a <see cref="Material"/> for a <see cref="CubismOffscreen"/>.
|
|
/// </summary>
|
|
/// <param name="sender">Event source.</param>
|
|
/// <param name="offscreen">Offscreen to pick for.</param>
|
|
/// <returns></returns>
|
|
public delegate Material OffscreenMaterialPicker(CubismModel3Json sender, CubismOffscreen offscreen);
|
|
|
|
#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];
|
|
|
|
|
|
var fadeInTime = -1.0f;
|
|
var fadeOutTime = -1.0f;
|
|
for (var j = 0; j < motionCount; j++)
|
|
{
|
|
// Reset fade time cache.
|
|
fadeInTime = -1.0f;
|
|
fadeOutTime = -1.0f;
|
|
|
|
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"))
|
|
{
|
|
fadeInTime = motionGroup.Get(j).Get("FadeInTime").ToFloat();
|
|
}
|
|
modelJson.FileReferences.Motions.Motions[i][j].FadeInTime = fadeInTime;
|
|
|
|
if (motionGroup.Get(j).GetMap(null).ContainsKey("FadeOutTime"))
|
|
{
|
|
fadeOutTime = motionGroup.Get(j).Get("FadeOutTime").ToFloat();
|
|
}
|
|
modelJson.FileReferences.Motions.Motions[i][j].FadeOutTime = fadeOutTime;
|
|
}
|
|
}
|
|
|
|
|
|
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.DrawableMaterialPicker, CubismBuiltinPickers.TexturePicker, CubismBuiltinPickers.OffscreenMaterialPicker, shouldImportAsOriginalWorkflow);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instantiates a <see cref="CubismMoc">model source</see> and a <see cref="CubismModel">model</see>.
|
|
/// </summary>
|
|
/// <param name="pickDrawableMaterial">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(DrawableMaterialPicker pickDrawableMaterial, TexturePicker pickTexture, OffscreenMaterialPicker pickOffscreenMaterial, 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);
|
|
|
|
if (model == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
model.name = Path.GetFileNameWithoutExtension(FileReferences.Moc);
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
// Add parameters and parts inspectors.
|
|
model.gameObject.AddComponent<CubismParametersInspector>().hideFlags = HideFlags.DontSaveInBuild;
|
|
model.gameObject.AddComponent<CubismPartsInspector>().hideFlags = HideFlags.DontSaveInBuild;
|
|
#endif
|
|
|
|
// Create renderers.
|
|
var rendererController = model.gameObject.AddComponent<CubismRenderController>();
|
|
|
|
var renderers = rendererController.Renderers;
|
|
|
|
var drawables = model.Drawables;
|
|
var offscreens = model.Offscreens;
|
|
|
|
var drawableRenderers = rendererController.DrawableRenderers;
|
|
var offscreenRenderers = rendererController.OffscreenRenderers;
|
|
|
|
if (renderers == null
|
|
|| drawables == null
|
|
|| drawableRenderers == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// Initialize materials.
|
|
for (var i = 0; i < drawableRenderers.Length; ++i)
|
|
{
|
|
var renderer = drawableRenderers[i];
|
|
|
|
renderer.Material = pickDrawableMaterial(this, drawables[i]);
|
|
renderer.ColorBlendType = drawables[i].ColorBlend;
|
|
renderer.AlphaBlendType = drawables[i].AlphaBlend;
|
|
}
|
|
|
|
for (var i = 0; i < offscreenRenderers?.Length; i++)
|
|
{
|
|
var renderer = offscreenRenderers[i];
|
|
|
|
renderer.Material = pickOffscreenMaterial(this, offscreens[i]);
|
|
renderer.ColorBlendType = offscreens[i].ColorBlend;
|
|
renderer.AlphaBlendType = offscreens[i].AlphaBlend;
|
|
}
|
|
|
|
|
|
// Initialize textures.
|
|
for (var i = 0; i < renderers.Length; ++i)
|
|
{
|
|
if (renderers[i].DrawObjectType != CubismModelTypes.DrawObjectType.Drawable)
|
|
{
|
|
continue;
|
|
}
|
|
renderers[i].MainTexture = pickTexture(this, drawables[i]);
|
|
}
|
|
|
|
|
|
if (model.Parts != null)
|
|
{
|
|
var parts = model.Parts;
|
|
|
|
// Create and initialize partColorsEditors.
|
|
for (int i = 0; i < parts.Length; i++)
|
|
{
|
|
var partColorsEditor = parts[i].gameObject.AddComponent<CubismPartColorsEditor>();
|
|
partColorsEditor.TryInitialize(model);
|
|
}
|
|
}
|
|
|
|
|
|
// 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 = parameters[i].Id;
|
|
for (int j = 0; j < cdi3Json.Parameters.Length; j++)
|
|
{
|
|
if (cdi3Json.Parameters[j].Id == parameters[i].Id)
|
|
{
|
|
cubismDisplayInfoParameterName.Name = cdi3Json.Parameters[j].Name;
|
|
break;
|
|
}
|
|
}
|
|
cubismDisplayInfoParameterName.DisplayName = string.Empty;
|
|
}
|
|
}
|
|
|
|
if (cdi3Json != null)
|
|
{
|
|
// Setting up the part name for display.
|
|
// Initialize groups.
|
|
var parts = model.Parts;
|
|
|
|
for (var i = 0; i < parts.Length; i++)
|
|
{
|
|
var cubismDisplayInfoPartNames = parts[i].gameObject.AddComponent<CubismDisplayInfoPartName>();
|
|
cubismDisplayInfoPartNames.Name = parts[i].Id;
|
|
for (int j = 0; j < cdi3Json.Parts.Length; j++)
|
|
{
|
|
if (cdi3Json.Parts[j].Id == parts[i].Id)
|
|
{
|
|
cubismDisplayInfoPartNames.Name = cdi3Json.Parts[j].Name;
|
|
break;
|
|
}
|
|
}
|
|
cubismDisplayInfoPartNames.DisplayName = string.Empty;
|
|
}
|
|
|
|
// Get combined parameter information
|
|
var combinedParameters = cdi3Json.CombinedParameters;
|
|
|
|
if (combinedParameters != null)
|
|
{
|
|
// Parameters are always combined in pairs of two.
|
|
const int combinedParameterCount = 2;
|
|
|
|
// Set up CubismDisplayInfoCombinedParameterInfo component.
|
|
var combinedParameterInfo = model.gameObject.AddComponent<CubismDisplayInfoCombinedParameterInfo>();
|
|
combinedParameterInfo.CombinedParameters = new CubismDisplayInfo3Json.CombinedParameter[combinedParameters.Length];
|
|
|
|
for (var index = 0; index < combinedParameters.Length; index++)
|
|
{
|
|
// Skip if the combined parameter is invalid.
|
|
if (combinedParameters[index].Ids == null || combinedParameters[index].Ids.Length != combinedParameterCount)
|
|
{
|
|
Debug.LogWarning($"The data contains invalid CombinedParameters in {model.Moc.name}.cdi3.json.");
|
|
continue;
|
|
}
|
|
|
|
var combinedParameterIds = combinedParameters[index].Ids;
|
|
|
|
// Set CombinedParameter.
|
|
combinedParameterInfo.CombinedParameters[index] = new CubismDisplayInfo3Json.CombinedParameter
|
|
{
|
|
HorizontalParameterId = combinedParameterIds[0],
|
|
VerticalParameterId = combinedParameterIds[1]
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add mask controller if required.
|
|
var anyMasked = false;
|
|
for (var i = 0; i < renderers.Length; i++)
|
|
{
|
|
var renderer = renderers[i];
|
|
switch (renderer.DrawObjectType)
|
|
{
|
|
case CubismModelTypes.DrawObjectType.Drawable:
|
|
if (renderer.Drawable.IsMasked)
|
|
{
|
|
anyMasked = true;
|
|
}
|
|
break;
|
|
case CubismModelTypes.DrawObjectType.Offscreen:
|
|
if (renderer.Offscreen.IsMasked)
|
|
{
|
|
anyMasked = true;
|
|
}
|
|
break;
|
|
default:
|
|
Debug.LogWarning($"Unknown draw object type {renderer.DrawObjectType} in {model.name}.model3.json.");
|
|
break;
|
|
}
|
|
|
|
if (anyMasked)
|
|
{
|
|
rendererController.HasMask = true;
|
|
|
|
rendererController.TryInitialize();
|
|
// Already have a mask controller, no need to add it again.
|
|
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
|
|
}
|
|
}
|