public MvdMotion CreateFrom([CanBeNull] CharacterImasMotionAsset bodyMotion, [CanBeNull] Avatar avatar, [CanBeNull] PmxModel mltdPmxModel, [CanBeNull] CharacterImasMotionAsset cameraMotion, [CanBeNull] ScenarioObject scenarioObject, int songPosition) { IReadOnlyList <MvdCameraMotion> cameraFrames; if (ProcessCameraFrames && cameraMotion != null) { cameraFrames = CreateCameraMotions(cameraMotion); } else { cameraFrames = EmptyArray.Of <MvdCameraMotion>(); } var mvd = new MvdMotion(cameraFrames); if (ConversionConfig.Current.Transform60FpsTo30Fps) { mvd.Fps = 30; } else { mvd.Fps = 60; } return(mvd); }
public static CameraAnimationSet <CharacterImasMotionAsset> LoadCamera([NotNull] string filePath, [CanBeNull] int?desiredCameraNumber) { CharacterImasMotionAsset cam = null, apa = null, apg = null, bpg = null; var manager = new AssetsManager(); manager.LoadFiles(filePath); var ser = new ScriptableObjectSerializer(); int cameraNumber; if (desiredCameraNumber != null) { do { // Some songs, e.g. Alliance Stardust (alstar), has more than one cameras. // In these cases the user must select the appropriate camera number. if (TryLoadSpecifiedCamera(manager, ser, desiredCameraNumber.Value, ref cam, ref apa, ref apg, ref bpg)) { cameraNumber = desiredCameraNumber.Value; break; } TryLoadFirstFoundCamera(manager, ser, ref cam, ref apa, ref apg, ref bpg); cameraNumber = InvalidCameraNumber; } while (false); } else { TryLoadFirstFoundCamera(manager, ser, ref cam, ref apa, ref apg, ref bpg); cameraNumber = UnspecifiedCameraNumber; } return(AnimationSet.CreateCamera(cameraNumber, cam, apg, apa, bpg)); }
public MvdMotion CreateCameraMotion([CanBeNull] CharacterImasMotionAsset mainCamera, [NotNull] ScenarioObject baseScenario, [CanBeNull] CharacterImasMotionAsset cameraAppeal, AppealType appealType) { MvdCameraMotion[] cameraFrames; if (ProcessCameraFrames && mainCamera != null) { cameraFrames = CreateCameraMotions(mainCamera, baseScenario, cameraAppeal, appealType); } else { cameraFrames = Array.Empty <MvdCameraMotion>(); } var mvd = new MvdMotion(cameraFrames); if (_conversionConfig.Transform60FpsTo30Fps) { mvd.Fps = FrameRate.Mmd; } else { mvd.Fps = FrameRate.Mltd; } return(mvd); }
public MvdMotion CreateCameraMotion([CanBeNull] CharacterImasMotionAsset cameraMotion) { IReadOnlyList <MvdCameraMotion> cameraFrames; if (ProcessCameraFrames && cameraMotion != null) { cameraFrames = CreateCameraMotions(cameraMotion); } else { cameraFrames = Array.Empty <MvdCameraMotion>(); } var mvd = new MvdMotion(cameraFrames); if (_conversionConfig.Transform60FpsTo30Fps) { mvd.Fps = FrameRate.Mmd; } else { mvd.Fps = FrameRate.Mltd; } return(mvd); }
private static DanceAnimationSet <CharacterImasMotionAsset> LoadDanceLegacy([NotNull] string filePath, int motionNumber, int formationNumber) { CharacterImasMotionAsset dan = null, apa = null, apg = null, bpg = null; var danComp = $"{motionNumber:00}_dan.imo"; var apaComp = $"{formationNumber:00}_apa.imo"; var apgComp = $"{formationNumber:00}_apg.imo"; var bpgComp = $"{formationNumber:00}_bpg.imo"; var manager = new AssetsManager(); manager.LoadFiles(filePath); var ser = new ScriptableObjectSerializer(); foreach (var assetFile in manager.assetsFileList) { foreach (var obj in assetFile.Objects) { if (obj.type != ClassIDType.MonoBehaviour) { continue; } var behaviour = obj as MonoBehaviour; if (behaviour == null) { throw new ArgumentException("An object serialized as MonoBehaviour is actually not a MonoBehaviour."); } // Can't use EndsWith() here: some bundles have ".asset" postfixes (alstar_01.imo.unity3d: _apa.imo, _dan_imo.asset, _apg.imo.asset) if (behaviour.m_Name.Contains(danComp)) { dan = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.Contains(apaComp)) { apa = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.Contains(apgComp)) { apg = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.Contains(bpgComp)) { bpg = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } if (dan != null && apa != null && apg != null && bpg != null) { break; } } } var suggestedPosition = GetSuggestedDancePosition(manager); return(AnimationSet.CreateDance(suggestedPosition, dan, apg, apa, bpg)); }
public static AnimationSet <CharacterImasMotionAsset> LoadCamera([NotNull] string filePath) { CharacterImasMotionAsset cam = null, apa = null, apg = null, bpg = null; const string defCamEnds = "_cam.imo"; const string apaCamEnds = "_apa.imo"; const string apgCamEnds = "_apg.imo"; const string bpgCamEnds = "_bpg.imo"; const string apaPortraitCamEnds = "_tate_apa.imo"; const string apgPortraitCamEnds = "_tate_apg.imo"; const string bpgPortraitCamEnds = "_tate_bpg.imo"; // should not exist var manager = new AssetsManager(); manager.LoadFiles(filePath); var ser = new ScriptableObjectSerializer(); foreach (var assetFile in manager.assetsFileList) { foreach (var obj in assetFile.Objects) { if (obj.type != ClassIDType.MonoBehaviour) { continue; } var behaviour = obj as MonoBehaviour; if (behaviour == null) { throw new ArgumentException("An object serialized as MonoBehaviour is actually not a MonoBehaviour."); } if (behaviour.m_Name.EndsWith(defCamEnds)) { cam = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.EndsWith(apaCamEnds) && !behaviour.m_Name.EndsWith(apaPortraitCamEnds)) { apa = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.EndsWith(apgCamEnds) && !behaviour.m_Name.EndsWith(apgPortraitCamEnds)) { apg = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.EndsWith(bpgCamEnds) && !behaviour.m_Name.EndsWith(bpgPortraitCamEnds)) { bpg = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } if (cam != null && apa != null && apg != null && bpg != null) { break; } } } return(AnimationSet.Create(cam, apg, apa, bpg)); }
public static AnimationClip CreateClipForCamera([NotNull] CharacterImasMotionAsset motion, [NotNull] string name) { var frames = ComputeKeyFrames(motion); var clip = CreateClipFromFrames(frames, name); return(clip); }
public static (CharacterImasMotionAsset, CharacterImasMotionAsset, CharacterImasMotionAsset) LoadDance() { CharacterImasMotionAsset dan = null, apa = null, apg = null; const string danComp = DancerPosition + "_dan"; const string apaComp = DancerPosition + "_apa"; const string apgComp = DancerPosition + "_apg"; var manager = new AssetsManager(); manager.LoadFiles(DanceDataFilePath); var ser = new ScriptableObjectSerializer(); foreach (var assetFile in manager.assetsFileList) { foreach (var obj in assetFile.Objects) { if (obj.type != ClassIDType.MonoBehaviour) { continue; } var behaviour = obj as MonoBehaviour; if (behaviour == null) { throw new ArgumentException("An object serialized as MonoBehaviour is actually not a MonoBehaviour."); } if (behaviour.m_Name.Contains(danComp)) { dan = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.EndsWith(apaComp)) { apa = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.EndsWith(apgComp)) { apg = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } if (dan != null && apa != null && apg != null) { break; } } } return(dan, apa, apg); }
private MvdCameraMotion[] CreateCameraMotions([NotNull] CharacterImasMotionAsset mainCamera, [NotNull] ScenarioObject baseScenario, [CanBeNull] CharacterImasMotionAsset cameraAppeal, AppealType appealType) { var camera = CreateCamera(); var cameraFrames = CreateFrames(mainCamera, baseScenario, cameraAppeal, appealType); var cameraPropertyFrames = CreatePropertyFrames(); var result = new MvdCameraMotion(camera, cameraFrames, cameraPropertyFrames); result.DisplayName = CameraName; result.EnglishName = CameraName; return(new[] { result }); }
public VmdMotion CreateFrom([CanBeNull] CharacterImasMotionAsset bodyMotion, [CanBeNull] Avatar avatar, [CanBeNull] PmxModel mltdPmxModel, [CanBeNull] CharacterImasMotionAsset cameraMotion, [CanBeNull] ScenarioObject scenarioObject, int songPosition) { IReadOnlyList <VmdBoneFrame> boneFrames; IReadOnlyList <VmdCameraFrame> cameraFrames; IReadOnlyList <VmdFacialFrame> facialFrames; IReadOnlyList <VmdLightFrame> lightFrames; if (ProcessBoneFrames && (bodyMotion != null && avatar != null && mltdPmxModel != null)) { boneFrames = CreateBoneFrames(bodyMotion, avatar, mltdPmxModel); } else { boneFrames = EmptyArray.Of <VmdBoneFrame>(); } if (ProcessCameraFrames && cameraMotion != null) { cameraFrames = CreateCameraFrames(cameraMotion, FixedFov); } else { cameraFrames = EmptyArray.Of <VmdCameraFrame>(); } if (ProcessFacialFrames && scenarioObject != null) { facialFrames = CreateFacialFrames(scenarioObject, songPosition); } else { facialFrames = EmptyArray.Of <VmdFacialFrame>(); } if (ProcessLightFrames && scenarioObject != null) { lightFrames = CreateLightFrames(scenarioObject); } else { lightFrames = EmptyArray.Of <VmdLightFrame>(); } const string modelName = "MODEL_00"; var vmd = new VmdMotion(modelName, boneFrames, facialFrames, cameraFrames, lightFrames, null); return(vmd); }
public static (CharacterImasMotionAsset, CharacterImasMotionAsset, CharacterImasMotionAsset) LoadDance([NotNull] string filePath, int songPosition) { CharacterImasMotionAsset dan = null, apa = null, apg = null; var danEnds = $"{songPosition:00}_dan.imo"; var apaEnds = $"{songPosition:00}_apa.imo"; var apgEnds = $"{songPosition:00}_apg.imo"; var ser = new MonoBehaviourSerializer(); using (var fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var bundle = new BundleFile(fileStream, false)) { foreach (var assetFile in bundle.AssetFiles) { foreach (var preloadData in assetFile.PreloadDataList) { if (preloadData.KnownType != KnownClassID.MonoBehaviour) { continue; } var behaviour = preloadData.LoadAsMonoBehaviour(true); if (behaviour.Name.EndsWith(danEnds)) { behaviour = preloadData.LoadAsMonoBehaviour(false); dan = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.Name.EndsWith(apaEnds)) { behaviour = preloadData.LoadAsMonoBehaviour(false); apa = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.Name.EndsWith(apgEnds)) { behaviour = preloadData.LoadAsMonoBehaviour(false); apg = ser.Deserialize <CharacterImasMotionAsset>(behaviour); } if (dan != null && apa != null && apg != null) { break; } } } } } return(dan, apa, apg); }
public VmdMotion CreateCameraMotion([CanBeNull] CharacterImasMotionAsset cameraMotion) { IReadOnlyList <VmdCameraFrame> frames; if (ProcessCameraFrames && cameraMotion != null) { frames = CreateCameraFrames(cameraMotion, FixedFov); } else { frames = null; } return(new VmdMotion(ModelName, null, null, frames, null, null)); }
public static (CharacterImasMotionAsset, CharacterImasMotionAsset, CharacterImasMotionAsset) LoadDance() { CharacterImasMotionAsset dan = null, apa = null, apg = null; var ser = new MonoBehaviourSerializer(); using (var fileStream = File.Open(DanceDataFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var bundle = new BundleFile(fileStream, false)) { foreach (var assetFile in bundle.AssetFiles) { foreach (var preloadData in assetFile.PreloadDataList) { if (preloadData.KnownType != KnownClassID.MonoBehaviour) { continue; } var behaviour = preloadData.LoadAsMonoBehaviour(true); switch (behaviour.Name) { case "dan_" + SongResourceName + "_" + DancerPosition + "_dan.imo": behaviour = preloadData.LoadAsMonoBehaviour(false); dan = ser.Deserialize <CharacterImasMotionAsset>(behaviour); break; case "dan_" + SongResourceName + "_" + DancerPosition + "_apa.imo": behaviour = preloadData.LoadAsMonoBehaviour(false); apa = ser.Deserialize <CharacterImasMotionAsset>(behaviour); break; case "dan_" + SongResourceName + "_" + DancerPosition + "_apg.imo": behaviour = preloadData.LoadAsMonoBehaviour(false); apg = ser.Deserialize <CharacterImasMotionAsset>(behaviour); break; } if (dan != null && apa != null && apg != null) { break; } } } } } return(dan, apa, apg); }
public static async UniTask <AnimationClip> CreateAsync([NotNull] CharacterImasMotionAsset motion, [NotNull] string name) { await UniTask.SwitchToThreadPool(); var frameDict = CreateGroupedFramesByPath(motion); await UniTask.SwitchToMainThread(); var clip = new AnimationClip(); clip.name = name; clip.frameRate = FrameRate.Mltd; ApplyClipFromGroupedFrames(clip, frameDict); return(clip); }
public static AnimationClip CreateIndirectClip([NotNull] CharacterImasMotionAsset motion, [NotNull] string name) { var focalLengthCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamBase" && curve.GetPropertyName() == "focalLength"); var camCutCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamBase" && curve.GetPropertyName() == "camCut"); var angleXCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamBaseS" && curve.GetPropertyType() == PropertyType.AngleX); var angleYCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamBaseS" && curve.GetPropertyType() == PropertyType.AngleY); var angleZCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamBaseS" && curve.GetPropertyType() == PropertyType.AngleZ); var posXCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamBaseS" && curve.GetPropertyType() == PropertyType.PositionX); var posYCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamBaseS" && curve.GetPropertyType() == PropertyType.PositionY); var posZCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamBaseS" && curve.GetPropertyType() == PropertyType.PositionZ); var targetXCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamTgtS" && curve.GetPropertyType() == PropertyType.PositionX); var targetYCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamTgtS" && curve.GetPropertyType() == PropertyType.PositionY); var targetZCurve = motion.curves.FirstOrDefault(curve => curve.path == "CamTgtS" && curve.GetPropertyType() == PropertyType.PositionZ); var focalLength = CreateCurve(focalLengthCurve); var angleZ = CreateCurve(angleZCurve); var posX = CreateCurve(posXCurve); var posY = CreateCurve(posYCurve); var posZ = CreateCurve(posZCurve); var targetX = CreateCurve(targetXCurve); var targetY = CreateCurve(targetYCurve); var targetZ = CreateCurve(targetZCurve); var clip = new AnimationClip(); clip.frameRate = FrameRate.Mltd; clip.name = name; clip.SetCurve(string.Empty, typeof(IndirectCamera), "focalLength", focalLength); clip.SetCurve(string.Empty, typeof(IndirectCamera), "angleZ", angleZ); clip.SetCurve(string.Empty, typeof(IndirectCamera), "positionX", posX); clip.SetCurve(string.Empty, typeof(IndirectCamera), "positionY", posY); clip.SetCurve(string.Empty, typeof(IndirectCamera), "positionZ", posZ); clip.SetCurve(string.Empty, typeof(IndirectCamera), "targetX", targetX); clip.SetCurve(string.Empty, typeof(IndirectCamera), "targetY", targetY); clip.SetCurve(string.Empty, typeof(IndirectCamera), "targetZ", targetZ); return(clip); }
public static CharacterImasMotionAsset LoadCamera([NotNull] string filePath) { CharacterImasMotionAsset cam = null; const string camEnds = "_cam.imo"; using (var fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var bundle = new BundleFile(fileStream, false)) { foreach (var assetFile in bundle.AssetFiles) { foreach (var preloadData in assetFile.PreloadDataList) { if (preloadData.KnownType != KnownClassID.MonoBehaviour) { continue; } var behaviour = preloadData.LoadAsMonoBehaviour(true); if (!behaviour.Name.EndsWith(camEnds)) { continue; } behaviour = preloadData.LoadAsMonoBehaviour(false); var ser = new MonoBehaviourSerializer(); cam = ser.Deserialize <CharacterImasMotionAsset>(behaviour); break; } } } } return(cam); }
private VmdCameraFrame[] CreateCameraFrames([CanBeNull] CharacterImasMotionAsset mainCamera, [NotNull] ScenarioObject baseScenario, [CanBeNull] CharacterImasMotionAsset cameraAppeal, uint fixedFov, AppealType appealType) { // Here we reuse the logic in MVD camera frame computation var mvdCreator = new MvdCreator(_conversionConfig, _scalingConfig) { ProcessCameraFrames = true, ProcessBoneFrames = false, ProcessFacialFrames = false, ProcessLightFrames = false, }; var mvdMotion = mvdCreator.CreateCameraMotion(mainCamera, baseScenario, cameraAppeal, appealType); var mvdFrames = mvdMotion.CameraMotions[0].CameraFrames; var cameraFrameList = new List <VmdCameraFrame>(); foreach (var mvdFrame in mvdFrames) { var vmdFrame = new VmdCameraFrame((int)mvdFrame.FrameNumber); vmdFrame.Length = mvdFrame.Distance * 0.1f; vmdFrame.Position = mvdFrame.Position; vmdFrame.Orientation = mvdFrame.Rotation + new Vector3(MathHelper.Pi, 0, MathHelper.Pi); // VMD does not have good support for animated FOV. So here just use a constant to avoid "jittering". // The drawback is, some effects (like the first zooming cut in Shooting Stars) will not be able to achieve. //var fov = FocalLengthToFov(frame.FocalLength); //vmdFrame.FieldOfView = (uint)fov; vmdFrame.FieldOfView = fixedFov; cameraFrameList.Add(vmdFrame); } return(cameraFrameList.ToArray()); }
private void DoWorkInternal([NotNull] object state) { Debug.Assert(InvokeRequired, "The worker procedure should be running on the worker thread."); Log("Worker started."); var p = (MainWorkerInputParams)state; var conversionConfig = PrepareConversionConfig(p); var scalingConfig = new ScalingConfig(conversionConfig); if (p.IdolHeight <= 0) { throw new ArgumentOutOfRangeException(nameof(p.IdolHeight), p.IdolHeight, "Invalid idol height."); } scalingConfig.CharacterHeight = p.IdolHeight; do { TransformHierarchies combinedHierarchies; // TODO: Do we still need this? CompositeAvatar combinedAvatar; CompositeMesh combinedMesh; int bodyMeshVertexCount; SwayController headSway; SwayController bodySway; if (p.GenerateModel || p.GenerateCharacterMotion) { Log("Loading body avatar..."); var bodyAvatar = ResourceLoader.LoadBodyAvatar(p.InputBody); if (bodyAvatar == null) { Log("Cannot load body avatar."); break; } Log("Loading body object hierarchies..."); var bodyHierarchies = ResourceLoader.LoadTransformHierarchies(p.InputBody); Log("Loading body mesh..."); var bodyMesh = ResourceLoader.LoadBodyMesh(p.InputBody); if (bodyMesh == null) { Log("Cannot load body mesh."); break; } Log("Loading head avatar..."); var headAvatar = ResourceLoader.LoadHeadAvatar(p.InputHead); if (headAvatar == null) { Log("Cannot load head avatar."); break; } Log("Loading head object hierarchies..."); var headHierarchies = ResourceLoader.LoadTransformHierarchies(p.InputHead); Log("Loading head mesh..."); var headMesh = ResourceLoader.LoadHeadMesh(p.InputHead); if (headMesh == null) { Log("Cannot load head mesh."); break; } Log("Loading head/body sway controllers..."); (bodySway, headSway) = ResourceLoader.LoadSwayControllers(p.InputBody, p.InputHead); if (bodySway == null || headSway == null) { Log("Cannot load sway controllers."); break; } Log("Combining avatars and meshes..."); combinedHierarchies = TransformHierarchies.Combine(bodyHierarchies, headHierarchies); combinedHierarchies = combinedHierarchies.PreserveMltdSpecificOnly(); combinedAvatar = CompositeAvatar.FromAvatars(bodyAvatar, headAvatar); combinedMesh = CompositeMesh.FromMeshes(bodyMesh, headMesh); bodyMeshVertexCount = bodyMesh.VertexCount; } else { combinedHierarchies = null; combinedAvatar = null; combinedMesh = null; bodyMeshVertexCount = 0; bodySway = null; headSway = null; } IBodyAnimationSource mainDance; IBodyAnimationSource danceAppeal = null; if (p.GenerateCharacterMotion) { Log("Loading dance motion..."); var loadedDance = ResourceLoader.LoadDance(p.InputDance, p.MotionNumber, p.FormationNumber); IBodyAnimationSource apSpecial, apAnother, apGorgeous; (mainDance, apSpecial, apAnother, apGorgeous) = (loadedDance.Default, loadedDance.Special, loadedDance.Another, loadedDance.Gorgeous); if (mainDance == null) { if (MltdAnimation.MinDance <= loadedDance.SuggestedPosition && loadedDance.SuggestedPosition <= MltdAnimation.MaxDance) { Log($"Cannot load dance data. However, this file may contain animation for idol using motion {loadedDance.SuggestedPosition.ToString()}. Please check whether you selected the correct motion number (in 'Motions' tab)."); } else { Log("Cannot load dance data. Please check whether you selected a dance data file."); } break; } if (p.AppealType != AppealType.None) { Log($"Trying to load dance appeal: {p.AppealType}"); switch (p.AppealType) { case AppealType.Special: { if (apSpecial != null) { danceAppeal = apSpecial; } break; } case AppealType.Another: { if (apAnother != null) { danceAppeal = apAnother; } break; } case AppealType.Gorgeous: { if (apGorgeous != null) { danceAppeal = apGorgeous; } break; } default: throw new ArgumentOutOfRangeException(); } if (danceAppeal == null) { Log($"Selected dance appeal for idol {p.FormationNumber.ToString()} is not found in the main dance data file. Trying with external file."); if (string.IsNullOrWhiteSpace(p.ExternalDanceAppealFile)) { Log("External dance appeal file is empty. Please set the path to the file."); break; } var externalAppealData = ResourceLoader.LoadDance(p.ExternalDanceAppealFile, p.MotionNumber, p.FormationNumber); (apSpecial, apAnother, apGorgeous) = (externalAppealData.Special, externalAppealData.Another, externalAppealData.Gorgeous); switch (p.AppealType) { case AppealType.Special: { if (apSpecial != null) { danceAppeal = apSpecial; } break; } case AppealType.Another: { if (apAnother != null) { danceAppeal = apAnother; } break; } case AppealType.Gorgeous: { if (apGorgeous != null) { danceAppeal = apGorgeous; } break; } default: throw new ArgumentOutOfRangeException(); } if (danceAppeal == null) { Log("Cannot load dance appeal data. Possible causes: 1) the file is not an appeal data file; 2) this song does not have the appeal you selected; 3) there is no corresponding appeal for selected position (e.g. each appeal of クルリウタ [Kururi Uta] only matches 3 out of 5 characters). Using default option (no appeal)."); } } } } else { mainDance = null; danceAppeal = null; } ScenarioObject baseScenario, landscapeScenario, portraitScenario; ScenarioObject formationInfo; if (p.GenerateCharacterMotion || p.GenerateLipSync || p.GenerateFacialExpressions || p.GenerateCameraMotion) { (baseScenario, landscapeScenario, portraitScenario) = ResourceLoader.LoadScenario(p.InputScenario); if (baseScenario == null) { Log("Cannot load base scenario object."); break; } if (landscapeScenario == null) { Log("Cannot load landscape scenario."); break; } if (portraitScenario == null) { Log("Cannot load portrait scenario."); break; } } else { baseScenario = null; landscapeScenario = null; portraitScenario = null; } if (p.GenerateCharacterMotion) { Debug.Assert(baseScenario != null, nameof(baseScenario) + " != null"); Debug.Assert(landscapeScenario != null, nameof(landscapeScenario) + " != null"); Debug.Assert(portraitScenario != null, nameof(portraitScenario) + " != null"); if (baseScenario.HasFormationChangeEvents()) { formationInfo = baseScenario; } else { Log("Main scenario object does not contain facial expressions. Trying with landscape and portrait."); if (landscapeScenario.HasFormationChangeEvents()) { Log("Using formation info from landscape."); formationInfo = landscapeScenario; } else if (portraitScenario.HasFormationChangeEvents()) { Log("Using formation info from portrait."); formationInfo = portraitScenario; } else { Log("No scenario object contains formation info."); break; } } } else { formationInfo = null; } ScenarioObject lipSyncInfo, facialExprInfo; if (p.GenerateLipSync || p.GenerateFacialExpressions) { Debug.Assert(baseScenario != null, nameof(baseScenario) + " != null"); Debug.Assert(landscapeScenario != null, nameof(landscapeScenario) + " != null"); Debug.Assert(portraitScenario != null, nameof(portraitScenario) + " != null"); if (p.GenerateLipSync) { lipSyncInfo = baseScenario; } else { lipSyncInfo = null; } if (p.GenerateFacialExpressions) { if (baseScenario.HasFacialExpressionEvents()) { facialExprInfo = baseScenario; } else { Log("Main scenario object does not contain facial expressions. Trying with landscape and portrait."); var foundFacialExpr = true; switch (p.PreferredFacialExpressionSource) { case MainWorkerInputParams.FallbackFacialExpressionSource.Landscape: { if (landscapeScenario.HasFacialExpressionEvents()) { facialExprInfo = landscapeScenario; Log("Using facial expressions: landscape."); } else if (portraitScenario.HasFacialExpressionEvents()) { Log("Facial expressions are not found in landscape, but found in portrait. Use portrait instead."); facialExprInfo = portraitScenario; } else { Log("No scenario object contains facial expressions."); facialExprInfo = null; foundFacialExpr = false; } break; } case MainWorkerInputParams.FallbackFacialExpressionSource.Portrait: if (portraitScenario.HasFacialExpressionEvents()) { facialExprInfo = portraitScenario; Log("Using facial expressions: portrait."); } else if (landscapeScenario.HasFacialExpressionEvents()) { Log("Facial expressions are not found in portrait, but found in landscape. Use landscape instead."); facialExprInfo = landscapeScenario; } else { Log("No scenario object contains facial expressions."); facialExprInfo = null; foundFacialExpr = false; } break; default: throw new ArgumentOutOfRangeException(nameof(p.PreferredFacialExpressionSource), p.PreferredFacialExpressionSource, "Invalid facial expression source."); } if (!foundFacialExpr) { break; } } } else { facialExprInfo = null; } } else { lipSyncInfo = null; facialExprInfo = null; } CharacterImasMotionAsset mainCamera; CharacterImasMotionAsset cameraAppeal = null; if (p.GenerateCameraMotion) { Log("Loading camera motion..."); CharacterImasMotionAsset apSpecial, apAnother, apGorgeous; var loadedCamera = ResourceLoader.LoadCamera(p.InputCamera, p.DesiredCameraNumber); (mainCamera, apSpecial, apAnother, apGorgeous) = (loadedCamera.Default, loadedCamera.Special, loadedCamera.Another, loadedCamera.Gorgeous); if (mainCamera == null) { Log("Cannot load camera data."); break; } if (p.DesiredCameraNumber.HasValue && loadedCamera.CameraNumber == ResourceLoader.InvalidCameraNumber) { Log($"Cannot find motion for camera #{p.DesiredCameraNumber.Value.ToString()}, using the first found motion."); } if (p.AppealType != AppealType.None) { Log($"Trying to load appeal for camera: {p.AppealType}"); switch (p.AppealType) { case AppealType.Special: { if (apSpecial != null) { cameraAppeal = apSpecial; } break; } case AppealType.Another: { if (apAnother != null) { cameraAppeal = apAnother; } break; } case AppealType.Gorgeous: { if (apGorgeous != null) { cameraAppeal = apGorgeous; } break; } default: throw new ArgumentOutOfRangeException(); } // Otherwise not possible; camera appeals are always stored in the main camera file if (cameraAppeal == null) { Log("Cannot load camera appeal data. Please check whether this song actually has the appeal you selected."); break; } } } else { mainCamera = null; } // And now the job starts! { PmxModel pmx; string texPrefix; BakedMaterial[] materialList; if (p.GenerateModel || p.GenerateCharacterMotion) { // Now file names are like "ch_pr001_201xxx.unity3d". var avatarName = (new FileInfo(p.InputHead).Name).Substring(3, 12); // ss001_015siz -> 015ss001 // Note: PMD allows max 19 characters in texture file names. // In the format below, textures will be named like: // tex\015ss001_01.png // which is at the limit. texPrefix = avatarName.Substring(6, 3) + avatarName.Substring(0, 5); // TODO: PMX seems to store path in this way. If so, MillionDance only works on Windows. texPrefix = $@"tex\{texPrefix}_"; Log("Generating model..."); Debug.Assert(combinedHierarchies != null); Debug.Assert(combinedMesh != null); var boneLookup = new BoneLookup(conversionConfig); var pmxCreator = new PmxCreator(conversionConfig, scalingConfig, boneLookup); var pmxConversionDetails = new PmxCreator.ConversionDetails(texPrefix, p.ApplyGameStyledToon, p.SkinToonNumber, p.ClothesToonNumber); pmx = pmxCreator.CreateModel(combinedAvatar, combinedMesh, bodyMeshVertexCount, bodySway, headSway, pmxConversionDetails, out materialList); } else { pmx = null; texPrefix = null; materialList = null; } if (p.GenerateModel) { Log("Saving model..."); Debug.Assert(pmx != null); using (var w = new PmxWriter(File.Open(p.OutputModel, FileMode.Create, FileAccess.Write, FileShare.Write))) { w.Write(pmx); } Log("Saving textures..."); var modelDir = (new FileInfo(p.OutputModel)).DirectoryName; Debug.Assert(modelDir != null); { var texDir = (new FileInfo(Path.Combine(modelDir, texPrefix))).DirectoryName; Debug.Assert(texDir != null); if (!Directory.Exists(texDir)) { Directory.CreateDirectory(texDir); } } foreach (var material in materialList) { var textureFilePath = Path.Combine(modelDir, material.TextureName); using (var memoryStream = new MemoryStream()) { material.BakedTexture.Save(memoryStream, ImageFormat.Png); using (var fileStream = File.Open(textureFilePath, FileMode.Create, FileAccess.Write, FileShare.Write)) { memoryStream.Position = 0; memoryStream.CopyTo(fileStream); } } } foreach (var material in materialList) { material.Dispose(); } } if (p.GenerateCharacterMotion) { Log("Generating character motion..."); var creator = new VmdCreator(conversionConfig, scalingConfig) { ProcessBoneFrames = true, ProcessCameraFrames = false, ProcessFacialFrames = false, ProcessLightFrames = false }; var danceVmd = creator.CreateDanceMotion(mainDance, baseScenario, formationInfo, combinedAvatar, pmx, danceAppeal, p.FormationNumber, p.AppealType); Log("Saving character motion..."); using (var w = new VmdWriter(File.Open(p.OutputCharacterAnimation, FileMode.Create, FileAccess.Write, FileShare.Write))) { w.Write(danceVmd); } } if (p.GenerateLipSync) { Log("Generating lip sync..."); var creator = new VmdCreator(conversionConfig, scalingConfig) { ProcessBoneFrames = false, ProcessCameraFrames = false, ProcessFacialFrames = true, ProcessLightFrames = false }; var lipVmd = creator.CreateLipSync(lipSyncInfo, p.FormationNumber, p.IgnoreSingControl); Log("Saving lip sync..."); using (var w = new VmdWriter(File.Open(p.OutputLipSync, FileMode.Create, FileAccess.Write, FileShare.Write))) { w.Write(lipVmd); } } if (p.GenerateFacialExpressions) { Log("Generating facial expressions..."); var creator = new VmdCreator(conversionConfig, scalingConfig) { ProcessBoneFrames = false, ProcessCameraFrames = false, ProcessFacialFrames = true, ProcessLightFrames = false }; var facialVmd = creator.CreateFacialExpressions(facialExprInfo, p.FormationNumber); Log("Saving facial expressions..."); using (var w = new VmdWriter(File.Open(p.OutputFacialExpressions, FileMode.Create, FileAccess.Write, FileShare.Write))) { w.Write(facialVmd); } } if (p.GenerateCameraMotion) { Log("Generating camera motion..."); if (p.UseMvdForCamera) { var creator = new MvdCreator(conversionConfig, scalingConfig) { ProcessBoneFrames = false, ProcessCameraFrames = true, ProcessFacialFrames = false, ProcessLightFrames = false, }; var motion = creator.CreateCameraMotion(mainCamera, baseScenario, cameraAppeal, p.AppealType); Log("Writing camera motion..."); using (var w = new MvdWriter(File.Open(p.OutputCamera, FileMode.Create, FileAccess.Write, FileShare.Write))) { w.Write(motion); } } else { var creator = new VmdCreator(conversionConfig, scalingConfig) { ProcessBoneFrames = false, ProcessCameraFrames = true, ProcessFacialFrames = false, ProcessLightFrames = false, }; creator.FixedFov = p.FixedFov; var motion = creator.CreateCameraMotion(mainCamera, baseScenario, cameraAppeal, p.AppealType); Log("Writing camera motion..."); using (var w = new VmdWriter(File.Open(p.OutputCamera, FileMode.Create, FileAccess.Write, FileShare.Write))) { w.Write(motion); } } } } } while (false); }
public static CameraAnimation CreateFrom([NotNull] CharacterImasMotionAsset cameraMotion) { var focalLengthCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamBase" && curve.GetPropertyName() == "focalLength"); var camCutCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamBase" && curve.GetPropertyName() == "camCut"); var angleXCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamBaseS" && curve.GetPropertyType() == PropertyType.AngleX); var angleYCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamBaseS" && curve.GetPropertyType() == PropertyType.AngleY); var angleZCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamBaseS" && curve.GetPropertyType() == PropertyType.AngleZ); var posXCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamBaseS" && curve.GetPropertyType() == PropertyType.PositionX); var posYCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamBaseS" && curve.GetPropertyType() == PropertyType.PositionY); var posZCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamBaseS" && curve.GetPropertyType() == PropertyType.PositionZ); var targetXCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamTgtS" && curve.GetPropertyType() == PropertyType.PositionX); var targetYCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamTgtS" && curve.GetPropertyType() == PropertyType.PositionY); var targetZCurve = cameraMotion.Curves.FirstOrDefault(curve => curve.Path == "CamTgtS" && curve.GetPropertyType() == PropertyType.PositionZ); var allCameraCurves = new[] { focalLengthCurve, camCutCurve, angleXCurve, angleYCurve, angleZCurve, posXCurve, posYCurve, posZCurve, targetXCurve, targetYCurve, targetZCurve }; if (AnyoneIsNull(allCameraCurves)) { throw new ApplicationException("Invalid camera motion file."); } Debug.Assert(focalLengthCurve != null, nameof(focalLengthCurve) + " != null"); Debug.Assert(camCutCurve != null, nameof(camCutCurve) + " != null"); Debug.Assert(angleXCurve != null, nameof(angleXCurve) + " != null"); Debug.Assert(angleYCurve != null, nameof(angleYCurve) + " != null"); Debug.Assert(angleZCurve != null, nameof(angleZCurve) + " != null"); Debug.Assert(posXCurve != null, nameof(posXCurve) + " != null"); Debug.Assert(posYCurve != null, nameof(posYCurve) + " != null"); Debug.Assert(posZCurve != null, nameof(posZCurve) + " != null"); Debug.Assert(targetXCurve != null, nameof(targetXCurve) + " != null"); Debug.Assert(targetYCurve != null, nameof(targetYCurve) + " != null"); Debug.Assert(targetZCurve != null, nameof(targetZCurve) + " != null"); if (!AllUseFCurve(allCameraCurves)) { throw new ApplicationException("Invalid key type."); } const float frameDuration = 1.0f / FrameRate.Mltd; var totalDuration = GetMaxDuration(allCameraCurves); var frameCount = (int)Math.Round(totalDuration / frameDuration); var cameraFrames = new CameraFrame[frameCount]; for (var i = 0; i < frameCount; ++i) { var frame = new CameraFrame(); var time = i * frameDuration; frame.Time = time; frame.FocalLength = GetInterpolatedValue(focalLengthCurve, time); frame.Cut = (int)GetLowerClampedValue(camCutCurve, time); frame.AngleX = GetInterpolatedValue(angleXCurve, time); frame.AngleY = GetInterpolatedValue(angleYCurve, time); frame.AngleZ = GetInterpolatedValue(angleZCurve, time); frame.PositionX = GetInterpolatedValue(posXCurve, time); frame.PositionY = GetInterpolatedValue(posYCurve, time); frame.PositionZ = GetInterpolatedValue(posZCurve, time); frame.TargetX = GetInterpolatedValue(targetXCurve, time); frame.TargetY = GetInterpolatedValue(targetYCurve, time); frame.TargetZ = GetInterpolatedValue(targetZCurve, time); cameraFrames[i] = frame; } return(new CameraAnimation(cameraFrames, totalDuration)); }
private static bool TryLoadFirstFoundCamera([NotNull] AssetsManager manager, [NotNull] ScriptableObjectSerializer serializer, [CanBeNull] ref CharacterImasMotionAsset cam, [CanBeNull] ref CharacterImasMotionAsset apa, [CanBeNull] ref CharacterImasMotionAsset apg, [CanBeNull] ref CharacterImasMotionAsset bpg) { const string defCamEnds = "_cam.imo"; const string apaCamEnds = "_apa.imo"; const string apgCamEnds = "_apg.imo"; const string bpgCamEnds = "_bpg.imo"; const string apaPortraitCamEnds = "_tate_apa.imo"; const string apgPortraitCamEnds = "_tate_apg.imo"; const string bpgPortraitCamEnds = "_tate_bpg.imo"; // should not exist foreach (var assetFile in manager.assetsFileList) { foreach (var obj in assetFile.Objects) { if (obj.type != ClassIDType.MonoBehaviour) { continue; } var behaviour = obj as MonoBehaviour; if (behaviour == null) { throw new ArgumentException("An object serialized as MonoBehaviour is actually not a MonoBehaviour."); } if (behaviour.m_Name.EndsWith(defCamEnds)) { cam = serializer.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.EndsWith(apaCamEnds) && !behaviour.m_Name.EndsWith(apaPortraitCamEnds)) { apa = serializer.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.EndsWith(apgCamEnds) && !behaviour.m_Name.EndsWith(apgPortraitCamEnds)) { apg = serializer.Deserialize <CharacterImasMotionAsset>(behaviour); } else if (behaviour.m_Name.EndsWith(bpgCamEnds) && !behaviour.m_Name.EndsWith(bpgPortraitCamEnds)) { bpg = serializer.Deserialize <CharacterImasMotionAsset>(behaviour); } if (cam != null && apa != null && apg != null && bpg != null) { break; } } } return(cam != null && apa != null && apg != null && bpg != null); }
private static Dictionary <string, List <DanceKeyFrame> > CreateGroupedFramesByPath([NotNull] CharacterImasMotionAsset motion) { var frameCount = motion.curves.Max(curve => curve.values.Length); var frameDict = new Dictionary <string, List <DanceKeyFrame> >(); foreach (var curve in motion.curves) { var path = curve.path; var keyType = curve.GetKeyType(); var propertyType = curve.GetPropertyType(); List <DanceKeyFrame> frameList; if (frameDict.ContainsKey(path)) { frameList = frameDict[path]; } else { frameList = new List <DanceKeyFrame>(); frameDict.Add(path, frameList); } switch (keyType) { case KeyType.Const: { switch (propertyType) { case PropertyType.AngleX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameList, frameIndex, path).AngleX = curve.values[0]; } break; case PropertyType.AngleY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameList, frameIndex, path).AngleY = curve.values[0]; } break; case PropertyType.AngleZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameList, frameIndex, path).AngleZ = curve.values[0]; } break; case PropertyType.PositionX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameList, frameIndex, path).PositionX = curve.values[0]; } break; case PropertyType.PositionY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameList, frameIndex, path).PositionY = curve.values[0]; } break; case PropertyType.PositionZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameList, frameIndex, path).PositionZ = curve.values[0]; } break; default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, $"Unknown property type: \"{propertyType}\"."); } } break; case KeyType.FullFrame: { var valueCount = curve.values.Length; switch (propertyType) { case PropertyType.AngleX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameList, frameIndex, path).AngleX = curve.values[index]; } break; case PropertyType.AngleY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameList, frameIndex, path).AngleY = curve.values[index]; } break; case PropertyType.AngleZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameList, frameIndex, path).AngleZ = curve.values[index]; } break; case PropertyType.PositionX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameList, frameIndex, path).PositionX = curve.values[index]; } break; case PropertyType.PositionY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameList, frameIndex, path).PositionY = curve.values[index]; } break; case PropertyType.PositionZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameList, frameIndex, path).PositionZ = curve.values[index]; } break; default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, $"Unknown property type: \"{((int)propertyType).ToString()}\"."); } break; } case KeyType.Discrete: { if ((curve.values.Length % 2) != 0) { throw new ApplicationException($"Length of curve values {curve.values.Length.ToString()} is not a multiple of 2."); } var curveValueCount = curve.values.Length / 2; var curTime = curve.values[0]; var curValue = curve.values[1]; var nextTime = curve.values[2]; var nextValue = curve.values[3]; var curIndex = 0; switch (propertyType) { case PropertyType.AngleX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameList, frameIndex, path); var value = InterpolateValue(frame.Time, curve, ref curIndex, curveValueCount, ref curTime, ref curValue, ref nextTime, ref nextValue); frame.AngleX = value; } break; case PropertyType.AngleY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameList, frameIndex, path); var value = InterpolateValue(frame.Time, curve, ref curIndex, curveValueCount, ref curTime, ref curValue, ref nextTime, ref nextValue); frame.AngleY = value; } break; case PropertyType.AngleZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameList, frameIndex, path); var value = InterpolateValue(frame.Time, curve, ref curIndex, curveValueCount, ref curTime, ref curValue, ref nextTime, ref nextValue); frame.AngleZ = value; } break; case PropertyType.PositionX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameList, frameIndex, path); var value = InterpolateValue(frame.Time, curve, ref curIndex, curveValueCount, ref curTime, ref curValue, ref nextTime, ref nextValue); frame.PositionX = value; } break; case PropertyType.PositionY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameList, frameIndex, path); var value = InterpolateValue(frame.Time, curve, ref curIndex, curveValueCount, ref curTime, ref curValue, ref nextTime, ref nextValue); frame.PositionY = value; } break; case PropertyType.PositionZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameList, frameIndex, path); var value = InterpolateValue(frame.Time, curve, ref curIndex, curveValueCount, ref curTime, ref curValue, ref nextTime, ref nextValue); frame.PositionZ = value; } break; default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, $"Unknown property type: \"{((int)propertyType).ToString()}\"."); } break; } default: throw new ArgumentOutOfRangeException(nameof(keyType), keyType, $"Unknown key type: \"{((int)keyType).ToString()}\"."); } } return(frameDict); }
private IReadOnlyList <MvdCameraMotion> CreateCameraMotions([NotNull] CharacterImasMotionAsset cameraMotion) { const string cameraName = "カメラ00"; // Localization error (Mr. mogg, please!), MUST NOT USE "Camera00"! MvdCameraObject CreateCamera() { var cam = new MvdCameraObject(); cam.DisplayName = cameraName; cam.EnglishName = cameraName; cam.Id = 0; return(cam); } IReadOnlyList <MvdCameraFrame> CreateFrames() { var animation = CameraAnimation.CreateFrom(cameraMotion); var animationFrameCount = animation.CameraFrames.Count; var cameraFrameList = new List <MvdCameraFrame>(); for (var i = 0; i < animationFrameCount; ++i) { if (_conversionConfig.Transform60FpsTo30Fps) { if (i % 2 == 1) { continue; } } int frameIndex; if (_conversionConfig.Transform60FpsTo30Fps) { frameIndex = i / 2; } else { frameIndex = i; } var frame = animation.CameraFrames[i]; var mvdFrame = new MvdCameraFrame(frameIndex); var pos = new Vector3(frame.PositionX, frame.PositionY, frame.PositionZ); pos = pos.FixUnityToOpenTK(); if (_conversionConfig.ScaleToVmdSize) { pos = pos * _scalingConfig.ScaleUnityToVmd; } mvdFrame.Position = pos; var target = new Vector3(frame.TargetX, frame.TargetY, frame.TargetZ); target = target.FixUnityToOpenTK(); if (_conversionConfig.ScaleToVmdSize) { target = target * _scalingConfig.ScaleUnityToVmd; } var delta = target - pos; mvdFrame.Distance = delta.Length; var lookAtMatrix = Matrix4.LookAt(pos, target, Vector3.UnitY); var q = lookAtMatrix.ExtractRotation(); var rot = q.DecomposeRad(); rot.Z = MathHelper.DegreesToRadians(frame.AngleZ); rot.Y = MathHelper.Pi - rot.Y; if (rot.Y < 0) { rot.Y += MathHelper.TwoPi; } if (delta.Z < 0) { rot.Y = -(MathHelper.Pi + rot.Y); } rot.Z = -rot.Z; if (rot.X < -MathHelper.PiOver2) { rot.X = rot.X + MathHelper.Pi; } else if (rot.X > MathHelper.PiOver2) { rot.X = -(MathHelper.Pi - rot.X); } mvdFrame.Rotation = rot; // MVD has good support of dynamic FOV. So here we can animate its value. var fov = FocalLengthToFov(frame.FocalLength); mvdFrame.FieldOfView = MathHelper.DegreesToRadians(fov); cameraFrameList.Add(mvdFrame); } return(cameraFrameList); } IReadOnlyList <MvdCameraPropertyFrame> CreatePropertyFrames() { var list = new List <MvdCameraPropertyFrame>(); var f = new MvdCameraPropertyFrame(0); f.Enabled = true; f.IsPerspective = true; f.Alpha = 1; f.EffectEnabled = true; f.DynamicFovEnabled = false; f.DynamicFovRate = 0.1f; f.DynamicFovCoefficient = 1; f.RelatedBoneId = -1; f.RelatedModelId = -1; list.Add(f); return(list); } // https://photo.stackexchange.com/questions/41273/how-to-calculate-the-fov-in-degrees-from-focal-length-or-distance float FocalLengthToFov(float focalLength) { // By experiments, MLTD seems to use a 15mm or 16mm camera. const float sensorSize = 12; // unit: mm, as the unit of MLTD camera frame is also mm var fovRad = 2 * (float)Math.Atan((sensorSize / 2) / focalLength); var fovDeg = MathHelper.RadiansToDegrees(fovRad); return(fovDeg); } var camera = CreateCamera(); var cameraFrames = CreateFrames(); var cameraPropertyFrames = CreatePropertyFrames(); var result = new MvdCameraMotion(camera, cameraFrames, cameraPropertyFrames); result.DisplayName = cameraName; result.EnglishName = cameraName; return(new[] { result }); }
private IReadOnlyList <VmdCameraFrame> CreateCameraFrames([NotNull] CharacterImasMotionAsset cameraMotion, uint fixedFov) { var animation = CameraAnimation.CreateFrom(cameraMotion); var animationFrameCount = animation.CameraFrames.Count; var cameraFrameList = new List <VmdCameraFrame>(); for (var i = 0; i < animationFrameCount; ++i) { if (_conversionConfig.Transform60FpsTo30Fps) { if (i % 2 == 1) { continue; } } int frameIndex; if (_conversionConfig.Transform60FpsTo30Fps) { frameIndex = i / 2; } else { frameIndex = i; } var frame = animation.CameraFrames[i]; var vmdFrame = new VmdCameraFrame(frameIndex); var pos = new Vector3(frame.PositionX, frame.PositionY, frame.PositionZ); pos = pos.FixUnityToOpenTK(); if (_conversionConfig.ScaleToVmdSize) { pos = pos * _scalingConfig.ScaleUnityToVmd; } vmdFrame.Position = pos; var target = new Vector3(frame.TargetX, frame.TargetY, frame.TargetZ); target = target.FixUnityToOpenTK(); if (_conversionConfig.ScaleToVmdSize) { target = target * _scalingConfig.ScaleUnityToVmd; } var delta = target - pos; vmdFrame.Length = delta.Length; var lookAtMatrix = Matrix4.LookAt(pos, target, Vector3.UnitY); var q = lookAtMatrix.ExtractRotation(); var rot = q.DecomposeRad(); rot.Z = MathHelper.DegreesToRadians(frame.AngleZ); rot.Y = MathHelper.Pi - rot.Y; if (rot.Y < 0) { rot.Y += MathHelper.TwoPi; } if (delta.Z < 0) { rot.Y = -(MathHelper.Pi + rot.Y); } if (rot.X < -MathHelper.PiOver2) { rot.X = rot.X + MathHelper.Pi; } else if (rot.X > MathHelper.PiOver2) { rot.X = -(MathHelper.Pi - rot.X); } rot.X = -rot.X; rot.Y -= MathHelper.Pi; vmdFrame.Orientation = rot; // VMD does not have good support for animated FOV. So here just use a constant to avoid "jittering". // The drawback is, some effects (like the first zooming cut in Shooting Stars) will not be able to achieve. //var fov = FocalLengthToFov(frame.FocalLength); //vmdFrame.FieldOfView = (uint)fov; vmdFrame.FieldOfView = fixedFov; cameraFrameList.Add(vmdFrame); } return(cameraFrameList); }
public static AnimationClip CreateFrom([NotNull] CharacterImasMotionAsset danceData) { var frameCount = danceData.curves.Max(curve => curve.values.Length); var frameDict = new Dictionary <string, List <KeyFrame> >(); for (var i = 0; i < danceData.curves.Length; ++i) { var curve = danceData.curves[i]; var path = curve.path; if (path.Contains("BODY_SCALE/")) { path = path.Replace("BODY_SCALE/", string.Empty); } var keyType = curve.GetKeyType(); var propertyType = curve.GetPropertyType(); List <KeyFrame> frameList; if (frameDict.ContainsKey(path)) { frameList = frameDict[path]; } else { frameList = new List <KeyFrame>(); frameDict.Add(path, frameList); } Func <int, KeyFrame> GetOrAddFrame = index => { KeyFrame frame; if (frameList.Count > index) { frame = frameList[index]; } else { frame = new KeyFrame(index, path); frameList.Add(frame); } return(frame); }; switch (keyType) { case KeyType.Const: { switch (propertyType) { case PropertyType.AngleX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).AngleX = curve.values[0]; } break; case PropertyType.AngleY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).AngleY = curve.values[0]; } break; case PropertyType.AngleZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).AngleZ = curve.values[0]; } break; case PropertyType.PositionX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).PositionX = curve.values[0]; } break; case PropertyType.PositionY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).PositionY = curve.values[0]; } break; case PropertyType.PositionZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).PositionZ = curve.values[0]; } break; default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, $"Unknown property type: \"{propertyType}\"."); } } break; case KeyType.FullFrame: { var valueCount = curve.values.Length; switch (propertyType) { case PropertyType.AngleX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).AngleX = curve.values[index]; } break; case PropertyType.AngleY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).AngleY = curve.values[index]; } break; case PropertyType.AngleZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).AngleZ = curve.values[index]; } break; case PropertyType.PositionX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).PositionX = curve.values[index]; } break; case PropertyType.PositionY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).PositionY = curve.values[index]; } break; case PropertyType.PositionZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).PositionZ = curve.values[index]; } break; default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, $"Unknown property type: \"{propertyType}\"."); } } break; case KeyType.Discrete: { if ((curve.values.Length % 2) != 0) { throw new ApplicationException($"Length of curve values {curve.values.Length} is not a muliple of 2."); } var curveValueCount = curve.values.Length / 2; var curTime = curve.values[0]; var curValue = curve.values[1]; var nextTime = curve.values[2]; var nextValue = curve.values[3]; var curIndex = 0; Func <KeyFrame, float> InterpolateValue = frame => { if (curIndex >= curveValueCount - 1) { return(curValue); } var frameTime = frame.Time; if (frameTime >= nextTime) { curTime = nextTime; curValue = nextValue; ++curIndex; if (curIndex < curveValueCount - 1) { nextTime = curve.values[(curIndex + 1) * 2]; nextValue = curve.values[(curIndex + 1) * 2 + 1]; } } if (curIndex >= curveValueCount - 1) { return(curValue); } var duration = nextTime - curTime; var delta = frameTime - curTime; var p = delta / duration; return(curValue * (1 - p) + nextValue * p); }; switch (propertyType) { case PropertyType.AngleX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.AngleX = value; } break; case PropertyType.AngleY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.AngleY = value; } break; case PropertyType.AngleZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.AngleZ = value; } break; case PropertyType.PositionX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.PositionX = value; } break; case PropertyType.PositionY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.PositionY = value; } break; case PropertyType.PositionZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.PositionZ = value; } break; default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, $"Unknown property type: \"{propertyType}\"."); } } break; default: throw new ArgumentOutOfRangeException(nameof(keyType), keyType, $"Unknown key type: \"{keyType}\"."); } } var clip = new AnimationClip(); clip.name = "Clip1"; foreach (var kv in frameDict) { var path = kv.Key; var frameList = kv.Value; var frameListCount = frameList.Count; if (frameListCount > 0 && frameList[0].HasPositions) { var posX = new Keyframe[frameListCount]; var posY = new Keyframe[frameListCount]; var posZ = new Keyframe[frameListCount]; for (var i = 0; i < frameListCount; ++i) { var frame = frameList[i]; posX[i] = new Keyframe(frame.Time, frame.PositionX.Value); posY[i] = new Keyframe(frame.Time, frame.PositionY.Value); posZ[i] = new Keyframe(frame.Time, frame.PositionZ.Value); } var curveX = new AnimationCurve(posX); var curveY = new AnimationCurve(posY); var curveZ = new AnimationCurve(posZ); clip.SetCurve(path, typeof(Transform), "localPosition.x", curveX); clip.SetCurve(path, typeof(Transform), "localPosition.y", curveY); clip.SetCurve(path, typeof(Transform), "localPosition.z", curveZ); } if (frameListCount > 0 && frameList[0].HasRotations) { var rotX = new Keyframe[frameListCount]; var rotY = new Keyframe[frameListCount]; var rotZ = new Keyframe[frameListCount]; var rotW = new Keyframe[frameListCount]; for (var i = 0; i < frameListCount; ++i) { var frame = frameList[i]; float rx = frame.AngleX.Value, ry = frame.AngleY.Value, rz = frame.AngleZ.Value; var q = Quaternion.Euler(rx, ry, rz); rotX[i] = new Keyframe(frame.Time, q.x); rotY[i] = new Keyframe(frame.Time, q.y); rotZ[i] = new Keyframe(frame.Time, q.z); rotW[i] = new Keyframe(frame.Time, q.w); } var curveX = new AnimationCurve(rotX); var curveY = new AnimationCurve(rotY); var curveZ = new AnimationCurve(rotZ); var curveW = new AnimationCurve(rotW); clip.SetCurve(path, typeof(Transform), "localRotation.x", curveX); clip.SetCurve(path, typeof(Transform), "localRotation.y", curveY); clip.SetCurve(path, typeof(Transform), "localRotation.z", curveZ); clip.SetCurve(path, typeof(Transform), "localRotation.w", curveW); } //for (var i = 0; i < frameListCount - 1; i++) { // var startFrame = frameList[i]; // var endFrame = frameList[i + 1]; // if (startFrame.HasPositions) { // clip.SetCurve(path, typeof(Transform), "localPosition.x", AnimationCurve.Linear(startFrame.Time, startFrame.PositionX.Value, endFrame.Time, endFrame.PositionX.Value)); // clip.SetCurve(path, typeof(Transform), "localPosition.y", AnimationCurve.Linear(startFrame.Time, startFrame.PositionY.Value, endFrame.Time, endFrame.PositionY.Value)); // clip.SetCurve(path, typeof(Transform), "localPosition.z", AnimationCurve.Linear(startFrame.Time, startFrame.PositionZ.Value, endFrame.Time, endFrame.PositionZ.Value)); // } // if (startFrame.HasRotations) { // float rx1 = startFrame.AngleX.Value, ry1 = startFrame.AngleY.Value, rz1 = startFrame.AngleZ.Value; // float rx2 = endFrame.AngleX.Value, ry2 = endFrame.AngleY.Value, rz2 = endFrame.AngleZ.Value; // var q1 = Quaternion.Euler(rx1, ry1, rz1); // var q2 = Quaternion.Euler(rx2, ry2, rz2); // clip.SetCurve(path, typeof(Transform), "localRotation.x", AnimationCurve.Linear(startFrame.Time, q1.x, endFrame.Time, q2.x)); // clip.SetCurve(path, typeof(Transform), "localRotation.y", AnimationCurve.Linear(startFrame.Time, q1.y, endFrame.Time, q2.y)); // clip.SetCurve(path, typeof(Transform), "localRotation.z", AnimationCurve.Linear(startFrame.Time, q1.z, endFrame.Time, q2.z)); // clip.SetCurve(path, typeof(Transform), "localRotation.w", AnimationCurve.Linear(startFrame.Time, q1.w, endFrame.Time, q2.w)); // } //} } clip.legacy = true; return(clip); }
private MvdCameraFrame[] CreateFrames([NotNull] CharacterImasMotionAsset mainCamera, [NotNull] ScenarioObject baseScenario, [CanBeNull] CharacterImasMotionAsset cameraAppeal, AppealType appealType) { var mainAnimation = CameraAnimation.CreateFrom(mainCamera); var appealAnimation = cameraAppeal != null?CameraAnimation.CreateFrom(cameraAppeal) : null; var animationFrameCount = mainAnimation.CameraFrames.Length; var cameraFrameList = new List <MvdCameraFrame>(); var appealTimes = appealType != AppealType.None ? AppealHelper.CollectAppealTimeInfo(baseScenario) : default; var transform60FpsTo30Fps = _conversionConfig.Transform60FpsTo30Fps; var scaleToVmdSize = _conversionConfig.ScaleToVmdSize; var unityToVmdScale = _scalingConfig.ScaleUnityToVmd; for (var mltdFrameIndex = 0; mltdFrameIndex < animationFrameCount; ++mltdFrameIndex) { if (transform60FpsTo30Fps) { if (mltdFrameIndex % 2 == 1) { continue; } } // When entering and leaving the appeal, there is also a camera control event (type 58) with `layer` > 0 (see CollectFormationChanges() for the meaning of `layer`). var shouldUseAppeal = appealType != AppealType.None && (appealTimes.StartFrame <= mltdFrameIndex && mltdFrameIndex < appealTimes.EndFrame) && appealAnimation != null; var animation = shouldUseAppeal ? appealAnimation : mainAnimation; int projectedFrameIndex; if (shouldUseAppeal) { var indexInAppeal = mltdFrameIndex - appealTimes.StartFrame; if (indexInAppeal >= appealAnimation.FrameCount) { indexInAppeal = appealAnimation.FrameCount - 1; } // `indexInAppeal`, unlike `mltdFrameIndex`, has not been scaled yet if (transform60FpsTo30Fps) { projectedFrameIndex = indexInAppeal / 2; } else { projectedFrameIndex = indexInAppeal; } } else { projectedFrameIndex = mltdFrameIndex; } var motionFrame = animation.CameraFrames[projectedFrameIndex]; int mvdFrameIndex; if (transform60FpsTo30Fps) { mvdFrameIndex = mltdFrameIndex / 2; } else { mvdFrameIndex = mltdFrameIndex; } var mvdFrame = new MvdCameraFrame(mvdFrameIndex); var position = new Vector3(motionFrame.PositionX, motionFrame.PositionY, motionFrame.PositionZ); position = position.FixUnityToMmd(); if (scaleToVmdSize) { position = position * unityToVmdScale; } mvdFrame.Position = position; var target = new Vector3(motionFrame.TargetX, motionFrame.TargetY, motionFrame.TargetZ); target = target.FixUnityToMmd(); if (scaleToVmdSize) { target = target * unityToVmdScale; } var delta = target - position; mvdFrame.Distance = delta.Length; var q = CameraOrientation.QuaternionLookAt(in position, in target, in Vector3.UnitY); var rotation = CameraOrientation.ComputeMmdOrientation(in q, motionFrame.AngleZ); mvdFrame.Rotation = rotation; // MVD has good support of dynamic FOV. So here we can animate its value. var fov = FocalLengthToFov(motionFrame.FocalLength); mvdFrame.FieldOfView = MathHelper.DegreesToRadians(fov); cameraFrameList.Add(mvdFrame); } return(cameraFrameList.ToArray()); }
private static IReadOnlyList <VmdBoneFrame> CreateBoneFrames([NotNull] CharacterImasMotionAsset bodyMotion, [NotNull] Avatar avatar, [NotNull] PmxModel pmx) { var mltdHierarchy = BoneUtils.BuildBoneHierarchy(avatar); var pmxHierarchy = BoneUtils.BuildBoneHierarchy(pmx); if (ConversionConfig.Current.AppendIKBones || ConversionConfig.Current.AppendEyeBones) { throw new NotSupportedException("Character motion frames generation (from MLTD) is not supported when appending bones (eyes and/or IK) is enabled."); } else { Debug.Assert(mltdHierarchy.Count == pmxHierarchy.Count, "Hierarchy number should be equal between MLTD and MMD."); } foreach (var mltdBone in mltdHierarchy) { mltdBone.Initialize(); } foreach (var pmxBone in pmxHierarchy) { pmxBone.Initialize(); } var animation = BodyAnimation.CreateFrom(bodyMotion); var boneCount = mltdHierarchy.Count; var animatedBoneCount = animation.BoneCount; var keyFrameCount = animation.KeyFrames.Count; do { void MarkNamedBone(string name) { var bone = pmx.Bones.FirstOrDefault(b => b.Name == name); if (bone != null) { bone.IsMltdKeyBone = true; } else { Debug.Print("Warning: trying to mark bone {0} as MLTD key bone but the bone is missing from the model.", name); } } var names1 = animation.KeyFrames.Take(animatedBoneCount) .Select(kf => kf.Path).ToArray(); var names = names1.Select(BoneUtils.GetVmdBoneNameFromBonePath).ToArray(); // Mark MLTD key bones. foreach (var name in names) { MarkNamedBone(name); } // Special cases MarkNamedBone("KUBI"); MarkNamedBone("頭"); } while (false); Debug.Assert(keyFrameCount % animatedBoneCount == 0, "keyFrameCount % animatedBoneCount == 0"); var iterationTimes = keyFrameCount / animatedBoneCount; var boneFrameList = new List <VmdBoneFrame>(); for (var i = 0; i < iterationTimes; ++i) { if (ConversionConfig.Current.Transform60FpsTo30Fps) { if (i % 2 == 1) { continue; } } var keyFrameIndexStart = i * animatedBoneCount; for (var j = 0; j < animatedBoneCount; ++j) { var keyFrame = animation.KeyFrames[keyFrameIndexStart + j]; var mltdBoneName = keyFrame.Path.Replace("BODY_SCALE/", string.Empty); var targetBone = mltdHierarchy.SingleOrDefault(bone => bone.Name == mltdBoneName); if (targetBone == null) { //throw new ArgumentException("Bone not found."); continue; // Shika doesn't have the "POSITION" bone. } BoneNode transferredBone = null; foreach (var kv in BoneAttachmentMap) { if (kv.Key == mltdBoneName) { transferredBone = mltdHierarchy.SingleOrDefault(bone => bone.Name == kv.Value); if (transferredBone == null) { throw new ArgumentException(); } break; } } if (keyFrame.HasPositions) { var x = keyFrame.PositionX.Value; var y = keyFrame.PositionY.Value; var z = keyFrame.PositionZ.Value; var t = new Vector3(x, y, z); t = t.FixUnityToOpenTK(); if (ConversionConfig.Current.ScaleToVmdSize) { t = t * ScalingConfig.ScaleUnityToPmx; } targetBone.LocalPosition = t; //if (transferredBone != null) { // transferredBone.LocalPosition = t; //} } if (keyFrame.HasRotations) { var x = keyFrame.AngleX.Value; var y = keyFrame.AngleY.Value; var z = keyFrame.AngleZ.Value; var q = UnityRotation.EulerDeg(x, y, z); q = q.FixUnityToOpenTK(); targetBone.LocalRotation = q; if (transferredBone != null) { transferredBone.LocalRotation = q; } } } foreach (var mltdBone in mltdHierarchy) { mltdBone.UpdateTransform(); } for (var j = 0; j < boneCount; ++j) { var pmxBone = pmxHierarchy[j]; var mltdBone = mltdHierarchy[j]; { var pb = pmx.Bones.FirstOrDefault(b => b.Name == pmxBone.Name); Debug.Assert(pb != null, $"PMX bone with the name \"{pmxBone.Name}\" should exist."); if (!pb.IsMltdKeyBone) { continue; } } var skinMatrix = mltdBone.SkinMatrix; var mPmxBindingPose = pmxBone.BindingPose; var mWorld = pmxBone.Parent?.WorldMatrix ?? Matrix4.Identity; // skinMatrix == inv(mPmxBindingPose) x mLocal x mWorld var mLocal = mPmxBindingPose * skinMatrix * mWorld.Inverted(); // Here, translation is in... world coords? WTF? var t = mLocal.ExtractTranslation(); var q = mLocal.ExtractRotation(); if (pmxBone.Parent != null) { t = t - (pmxBone.InitialPosition - pmxBone.Parent.InitialPosition); } int frameIndex; if (ConversionConfig.Current.Transform60FpsTo30Fps) { frameIndex = i / 2; } else { frameIndex = i; } var vmdBoneName = BoneUtils.GetVmdBoneNameFromBoneName(mltdBone.Path); var boneFrame = new VmdBoneFrame(frameIndex, vmdBoneName); boneFrame.Position = t; boneFrame.Rotation = q; boneFrameList.Add(boneFrame); pmxBone.LocalPosition = t; pmxBone.LocalRotation = q; pmxBone.UpdateTransform(); } } return(boneFrameList); }
public static BodyAnimation CreateFrom([NotNull] CharacterImasMotionAsset danceMotion) { var curves = danceMotion.Curves; var frameCount = curves.Max(curve => curve.Values.Length); var frameDict = new Dictionary <string, List <KeyFrame> >(); for (var kk = 0; kk < curves.Length; ++kk) { var curve = curves[kk]; List <KeyFrame> frameList; if (frameDict.ContainsKey(curve.Path)) { frameList = frameDict[curve.Path]; } else { frameList = new List <KeyFrame>(); frameDict.Add(curve.Path, frameList); } var path = curve.Path; var keyType = curve.GetKeyType(); var propertyType = curve.GetPropertyType(); KeyFrame GetOrAddFrame(int index) { KeyFrame frame; if (frameList.Count > index) { frame = frameList[index]; } else { frame = new KeyFrame(index, path); frameList.Add(frame); } return(frame); } switch (keyType) { case KeyType.Const: { switch (propertyType) { case PropertyType.AngleX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).AngleX = curve.Values[0]; } break; case PropertyType.AngleY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).AngleY = curve.Values[0]; } break; case PropertyType.AngleZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).AngleZ = curve.Values[0]; } break; case PropertyType.PositionX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).PositionX = curve.Values[0]; } break; case PropertyType.PositionY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).PositionY = curve.Values[0]; } break; case PropertyType.PositionZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { GetOrAddFrame(frameIndex).PositionZ = curve.Values[0]; } break; default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, $"Unknown property type: \"{propertyType}\"."); } } break; case KeyType.FullFrame: { var valueCount = curve.Values.Length; switch (propertyType) { case PropertyType.AngleX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).AngleX = curve.Values[index]; } break; case PropertyType.AngleY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).AngleY = curve.Values[index]; } break; case PropertyType.AngleZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).AngleZ = curve.Values[index]; } break; case PropertyType.PositionX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).PositionX = curve.Values[index]; } break; case PropertyType.PositionY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).PositionY = curve.Values[index]; } break; case PropertyType.PositionZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var index = frameIndex < valueCount ? frameIndex : valueCount - 1; GetOrAddFrame(frameIndex).PositionZ = curve.Values[index]; } break; default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, $"Unknown property type: \"{propertyType}\"."); } } break; case KeyType.Discrete: { if ((curve.Values.Length % 2) != 0) { throw new ApplicationException($"Length of curve values {curve.Values.Length} is not a muliple of 2."); } var curveValueCount = curve.Values.Length / 2; var curTime = curve.Values[0]; var curValue = curve.Values[1]; var nextTime = curve.Values[2]; var nextValue = curve.Values[3]; var curIndex = 0; float InterpolateValue(KeyFrame frame) { if (curIndex >= curveValueCount - 1) { return(curValue); } var frameTime = frame.Time; if (frameTime >= nextTime) { curTime = nextTime; curValue = nextValue; ++curIndex; if (curIndex < curveValueCount - 1) { nextTime = curve.Values[(curIndex + 1) * 2]; nextValue = curve.Values[(curIndex + 1) * 2 + 1]; } } if (curIndex >= curveValueCount - 1) { return(curValue); } var duration = nextTime - curTime; var delta = frameTime - curTime; var p = delta / duration; return(curValue * (1 - p) + nextValue * p); } switch (propertyType) { case PropertyType.AngleX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.AngleX = value; } break; case PropertyType.AngleY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.AngleY = value; } break; case PropertyType.AngleZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.AngleZ = value; } break; case PropertyType.PositionX: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.PositionX = value; } break; case PropertyType.PositionY: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.PositionY = value; } break; case PropertyType.PositionZ: for (var frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var frame = GetOrAddFrame(frameIndex); var value = InterpolateValue(frame); frame.PositionZ = value; } break; default: throw new ArgumentOutOfRangeException(nameof(propertyType), propertyType, $"Unknown property type: \"{propertyType}\"."); } } break; default: throw new ArgumentOutOfRangeException(nameof(keyType), keyType, $"Unknown key type: \"{keyType}\"."); } } var totalList = frameDict.SelectMany(kv => kv.Value).ToList(); totalList.Sort((f1, f2) => { var s1 = f1.FrameIndex.CompareTo(f2.FrameIndex); if (s1 != 0) { return(s1); } return(string.Compare(f1.Path, f2.Path, StringComparison.Ordinal)); }); return(new BodyAnimation(totalList.ToArray(), danceMotion.Duration, frameDict.Count)); }
public VmdMotion CreateCameraMotion([CanBeNull] CharacterImasMotionAsset mainCamera, [NotNull] ScenarioObject baseScenario, [CanBeNull] CharacterImasMotionAsset cameraAppeal, AppealType appealType) { VmdCameraFrame[] frames; if (ProcessCameraFrames && mainCamera != null) { frames = CreateCameraFrames(mainCamera, baseScenario, cameraAppeal, FixedFov, appealType); } else { frames = null; } return(new VmdMotion(CameraName, null, null, frames, null, null)); }