Beispiel #1
0
        public static DanceAnimationSet <IBodyAnimationSource> LoadDance([NotNull] string filePath, int motionNumber, int formationNumber)
        {
            IBodyAnimationSource defaultSource = null, anotherSource = null, specialSource = null, gorgeousSource = null;
            bool anyAnimationLoaded;
            int  suggestedPosition;

            // About number of main dance animations and special/another appeal animations:
            // Most songs have 1 dance animation (i.e. animation for 1 idol, multiplied by 3/4/5 etc.) and 1 appeal animation
            // (i.e. for 1 idol when player completes FC before the big note). Or, n dance animation and n appeal animations
            // (e.g. 虹色letters [nijile], n=2 for position 1 and 2). Some songs have 1 dance animation and n appeal animations
            // (e.g. クルリウタ [kururi], n=3 for position 1, 2 and 3). Rarely a song has n dance animations and 1 appeal animation
            // (i.e. RE@DY!! [ready0], n=5). For each dance animation, there is a dan_ object; for each appeal animation, there
            // is an apa_ or apg_ object. So there isn't really a guarantee when dan, apa or apg is non-null.

            {
                // First try with legacy bundles
                var loaded = LoadDanceLegacy(filePath, motionNumber, formationNumber);

                suggestedPosition  = loaded.SuggestedPosition;
                anyAnimationLoaded = loaded.Default != null || loaded.Special != null || loaded.Another != null || loaded.Gorgeous != null;

                if (loaded.Default != null)
                {
                    defaultSource = new LegacyBodyAnimationSource(loaded.Default);
                }

                if (loaded.Special != null)
                {
                    specialSource = new LegacyBodyAnimationSource(loaded.Special);
                }

                if (loaded.Another != null)
                {
                    anotherSource = new LegacyBodyAnimationSource(loaded.Another);
                }

                if (loaded.Gorgeous != null)
                {
                    gorgeousSource = new LegacyBodyAnimationSource(loaded.Gorgeous);
                }
            }

            if (!anyAnimationLoaded)
            {
                // If failed, try the new one (from ~mid 2018?)
                var loaded = LoadDanceCompiled(filePath, motionNumber, formationNumber);

                suggestedPosition = loaded.SuggestedPosition;

                if (loaded.Default != null)
                {
                    defaultSource = new CompiledBodyAnimationSource(loaded.Default);
                }

                if (loaded.Special != null)
                {
                    specialSource = new CompiledBodyAnimationSource(loaded.Special);
                }

                if (loaded.Another != null)
                {
                    anotherSource = new CompiledBodyAnimationSource(loaded.Another);
                }

                if (loaded.Gorgeous != null)
                {
                    gorgeousSource = new CompiledBodyAnimationSource(loaded.Gorgeous);
                }
            }

            var animationSet = AnimationSet.CreateDance(suggestedPosition, defaultSource, specialSource, anotherSource, gorgeousSource);

            return(animationSet);
        }
Beispiel #2
0
        public static LoadedDance LoadDance([NotNull] string filePath, int songPosition)
        {
            IBodyAnimationSource danSource = null, apaSource = null, apgSource = null;
            var danceAnimationLoaded = false;
            var suggestedPosition    = InvalidDancePosition;

            // About number of main dance animations and special/another appeal animations:
            // Most songs have 1 dance animation (i.e. animation for 1 idol, multiplied by 3/4/5 etc.) and 1 appeal animation
            // (i.e. for 1 idol when player completes FC before the big note). Or, n dance animation and n appeal animations
            // (e.g. 虹色letters [nijile], n=2 for position 1 and 2). Some songs have 1 dance animation and n appeal animations
            // (e.g. クルリウタ [kururi], n=3 for position 1, 2 and 3). Rarely a song has n dance animations and 1 appeal animation
            // (i.e. RE@DY!! [ready0], n=5). For each dance animation, there is a dan_ object; for each appeal animation, there
            // is an apa_ or apg_ object. So there isn't really a guarantee when dan, apa or apg is non-null.

            if (!danceAnimationLoaded)
            {
                // First try with legacy bundles
                var(dan, apa, apg) = LoadDanceLegacy(filePath, songPosition, out suggestedPosition);

                if (dan != null)
                {
                    danSource            = new LegacyBodyAnimationSource(dan);
                    danceAnimationLoaded = true;
                }

                if (apa != null)
                {
                    apaSource = new LegacyBodyAnimationSource(apa);
                }

                if (apg != null)
                {
                    apgSource = new LegacyBodyAnimationSource(apg);
                }
            }

            if (!danceAnimationLoaded)
            {
                // If failed, try the new one (from ~mid 2018?)
                var(dan, apa, apg) = LoadDanceCompiled(filePath, songPosition, out suggestedPosition);

                if (dan != null)
                {
                    danSource            = new CompiledBodyAnimationSource(dan);
                    danceAnimationLoaded = true;
                }

                if (apa != null)
                {
                    apaSource = new CompiledBodyAnimationSource(apa);
                }

                if (apg != null)
                {
                    apgSource = new CompiledBodyAnimationSource(apg);
                }
            }

            var animationSet = AnimationSet.Create(danSource, apaSource, apgSource);

            return(new LoadedDance(animationSet, suggestedPosition));
        }
Beispiel #3
0
        // Some heavy stuff
        private void Initialize1()
        {
            ResHelper.LoadHeadMesh();

            _bodyMesh   = ResHelper.LoadBodyMesh();
            _bodyAvatar = ResHelper.LoadBodyAvatar();
            _headMesh   = ResHelper.LoadHeadMesh();
            _headAvatar = ResHelper.LoadHeadAvatar();

            Debug.Assert(_bodyAvatar != null, nameof(_bodyAvatar) + " != null");
            Debug.Assert(_bodyMesh != null, nameof(_bodyMesh) + " != null");
            Debug.Assert(_headAvatar != null, nameof(_headAvatar) + " != null");
            Debug.Assert(_headMesh != null, nameof(_headMesh) + " != null");

            _bodyBoneList = ResHelper.BuildBoneHierachy(_bodyAvatar, _bodyMesh);
            _headBoneList = ResHelper.BuildBoneHierachy(_headAvatar, _headMesh);

            do
            {
                // Fix "KUBI" (neck) bone's parent
                var kubiParent = _bodyBoneList.SingleOrDefault(bn => bn.Path == "MODEL_00/BASE/MUNE1/MUNE2/KUBI");
                var kubiBone   = _headBoneList.SingleOrDefault(bn => bn.Path == "KUBI");

                Debug.Assert(kubiParent != null);
                Debug.Assert(kubiBone != null);

                kubiParent.AddChild(kubiBone);

                Debug.Assert(kubiBone.Parent != null);

                // Don't forget to remove it from its old parent (or it will be updated twice from two parents).
                // The original parents and grandparents of KUBI are not needed; they are just like model anchors and shouldn't be animated.
                // See decompiled model for more information.
                kubiBone.Parent.RemoveChild(kubiBone);

                kubiBone.Parent = kubiParent;

                // Set its new initial parameters.
                // Since the two bones (KUBI and MODEL_00/BASE/MUNE1/MUNE2/KUBI) actually share the exact same transforms,
                // set its local transform to identity (t=0, q=0).
                kubiBone.InitialPosition = Vector3.Zero;
                kubiBone.InitialRotation = Quaternion.Identity;
                kubiBone.LocalPosition   = Vector3.Zero;
                kubiBone.LocalRotation   = Quaternion.Identity;

                // Now inform the rest of the head bones the new bone hierarchy. Force them to do so.
                foreach (var bone in _headBoneList)
                {
                    bone.Initialize(true);
                }
            } while (false);

            // TODO: FIXME: new format is not supported here (it's hard-coded) but it is handled in MillionDance.
            (_danceData, _, _) = ResHelper.LoadDance();

#if DEBUG
            do
            {
                var influencingBones = _bodyBoneList.Where((_, i) => _bodyMesh.Skin.Any(sk => sk.Any(a => a.BoneIndex == i)));

                Debug.Print("Bones that influences the mesh:");

                foreach (var bone in influencingBones)
                {
                    Debug.Print("#{0} \"{1}\"", bone.Index, bone.Path);
                }
            } while (false);
#endif

            _animation = new LegacyBodyAnimationSource(_danceData).Convert();

#if DEBUG
            do
            {
                var animatedBoneNames = _animation.KeyFrames.Select(f => f.Path).Distinct();

                Debug.Print("Animated bone names:");

                foreach (var boneName in animatedBoneNames)
                {
                    Debug.Print(boneName);
                }
            } while (false);
#endif

            _initialized1 = true;
        }