Пример #1
0
        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);
        }
Пример #2
0
        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));
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        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));
        }
Пример #6
0
        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));
        }
Пример #7
0
        public static AnimationClip CreateClipForCamera([NotNull] CharacterImasMotionAsset motion, [NotNull] string name)
        {
            var frames = ComputeKeyFrames(motion);
            var clip   = CreateClipFromFrames(frames, name);

            return(clip);
        }
Пример #8
0
        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);
        }
Пример #9
0
        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 });
        }
Пример #10
0
        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);
        }
Пример #11
0
        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);
        }
Пример #12
0
        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));
        }
Пример #13
0
        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);
        }
Пример #14
0
        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);
        }
Пример #15
0
        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);
        }
Пример #16
0
        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);
        }
Пример #17
0
        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());
        }
Пример #18
0
        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);
        }
Пример #19
0
        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));
        }
Пример #20
0
        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);
        }
Пример #21
0
        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);
        }
Пример #22
0
        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 });
        }
Пример #23
0
        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);
        }
Пример #24
0
        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);
        }
Пример #25
0
        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);
        }
Пример #27
0
        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));
        }
Пример #28
-1
        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));
        }