ソースを参照

Add gravity manipulation

Closes #3

UI could use a bit of tweaking
habeebweeb 4 年 前
コミット
33be17ef9d

+ 5 - 0
COM3D2.MeidoPhotoStudio.Plugin/Config/MeidoPhotoStudio/Translations/en/translation.ui.json

@@ -121,6 +121,11 @@
         "shiftPanties": "Shift",
         "detail": "Detailed Clothing"
     },
+    "gravityControlPane": {
+        "hairToggle": "Hair",
+        "skirtToggle": "Skirt",
+        "globalToggle": "Global"
+    },
     "handPane": {
         "header": "Hand Preset",
         "saveToggle": "Save Hand",

+ 10 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/DragPoint/DragPointOther.cs

@@ -67,4 +67,14 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             base.OnDestroy();
         }
     }
+
+    internal class DragPointGravity : DragPointGeneral
+    {
+        protected override void ApplyDragType() => ApplyProperties(Moving, Moving, false);
+        protected override void OnDestroy()
+        {
+            GameObject.Destroy(MyGameObject);
+            base.OnDestroy();
+        }
+    }
 }

+ 7 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/MainWindowPanes/PoseWindowPane.cs

@@ -15,6 +15,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         private MaidFaceLookPane maidFaceLookPane;
         private MpnAttachPropPane mpnAttachPropPane;
         private MaidDressingPane maidDressingPane;
+        private GravityControlPane gravityControlPane;
         private CopyPosePane copyPosePane;
         private HandPresetPane handPresetPane;
         private SaveHandPane saveHandPane;
@@ -51,6 +52,8 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 
             this.maidIKPane = AddPane(new MaidIKPane(meidoManager));
 
+            this.gravityControlPane = AddPane(new GravityControlPane(meidoManager));
+
             this.copyPosePane = AddPane(new CopyPosePane(meidoManager));
 
             this.saveHandToggle = new Toggle(Translation.Get("handPane", "saveToggle"));
@@ -100,6 +103,10 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 
             maidIKPane.Draw();
 
+            MiscGUI.WhiteLine();
+
+            this.gravityControlPane.Draw();
+
             GUI.enabled = this.meidoManager.HasActiveMeido;
             MiscGUI.Header(this.handPresetHeader);
             MiscGUI.WhiteLine();

+ 84 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/PoseWindowPanes/GravityControlPane.cs

@@ -0,0 +1,84 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace COM3D2.MeidoPhotoStudio.Plugin
+{
+    internal class GravityControlPane : BasePane
+    {
+        private MeidoManager meidoManager;
+        private Toggle hairToggle;
+        private Toggle skirtToggle;
+        private Toggle globalToggle;
+
+        public GravityControlPane(MeidoManager meidoManager)
+        {
+            this.meidoManager = meidoManager;
+
+            hairToggle = new Toggle(Translation.Get("gravityControlPane", "hairToggle"));
+            hairToggle.ControlEvent += (s, a) => ToggleGravity(hairToggle.Value, skirt: false);
+
+            skirtToggle = new Toggle(Translation.Get("gravityControlPane", "skirtToggle"));
+            skirtToggle.ControlEvent += (s, a) => ToggleGravity(skirtToggle.Value, skirt: true);
+
+            globalToggle = new Toggle(Translation.Get("gravityControlPane", "globalToggle"));
+            globalToggle.ControlEvent += (s, a) => SetGlobalGravity(globalToggle.Value);
+        }
+
+        public override void Draw()
+        {
+            bool enabled = this.meidoManager.HasActiveMeido;
+            GUI.enabled = enabled;
+
+            Meido meido = this.meidoManager.ActiveMeido;
+            GUILayout.BeginHorizontal();
+
+            GUI.enabled = enabled && meido.HairGravityValid;
+            hairToggle.Draw();
+
+            GUI.enabled = enabled && meido.SkirtGravityValid;
+            skirtToggle.Draw();
+
+            GUI.enabled = true;
+            GUILayout.EndHorizontal();
+
+            globalToggle.Draw();
+
+            GUI.enabled = true;
+        }
+
+        public override void UpdatePane()
+        {
+            if (!this.meidoManager.HasActiveMeido) return;
+
+            Meido meido = meidoManager.ActiveMeido;
+
+            this.updating = true;
+
+            hairToggle.Value = meido.HairGravityActive;
+            skirtToggle.Value = meido.SkirtGravityActive;
+
+            this.updating = false;
+        }
+
+        private void ToggleGravity(bool value, bool skirt = false)
+        {
+            if (updating) return;
+
+            if (this.meidoManager.GlobalGravity)
+            {
+                foreach (Meido meido in this.meidoManager.ActiveMeidoList)
+                {
+                    if (skirt) meido.SkirtGravityActive = value;
+                    else meido.HairGravityActive = value;
+                }
+            }
+            else
+            {
+                if (skirt) this.meidoManager.ActiveMeido.SkirtGravityActive = value;
+                else this.meidoManager.ActiveMeido.HairGravityActive = value;
+            }
+        }
+
+        private void SetGlobalGravity(bool value) => this.meidoManager.GlobalGravity = value;
+    }
+}

+ 33 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Managers/MeidoManager.cs

@@ -26,6 +26,26 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             private set => selectedMeido = Utility.Bound(value, 0, ActiveMeidoList.Count - 1);
         }
         public bool Busy => ActiveMeidoList.Any(meido => meido.Busy);
+        private bool globalGravity = false;
+        public bool GlobalGravity
+        {
+            get => globalGravity;
+            set
+            {
+                this.globalGravity = value;
+                Meido activeMeido = ActiveMeido;
+                int activeMeidoSlot = activeMeido.Slot;
+
+                foreach (Meido meido in ActiveMeidoList)
+                {
+                    if (meido.Slot != activeMeidoSlot)
+                    {
+                        meido.HairGravityActive = value ? activeMeido.HairGravityActive : false;
+                        meido.SkirtGravityActive = value ? activeMeido.SkirtGravityActive : false;
+                    }
+                }
+            }
+        }
 
         public void ChangeMaid(int index)
         {
@@ -49,6 +69,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             foreach (Meido meido in meidos)
             {
                 meido.UpdateMeido -= OnUpdateMeido;
+                meido.GravityMove -= OnGravityMove;
                 meido.Deactivate();
             }
             SelectMeidoList.Clear();
@@ -97,6 +118,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             foreach (Meido meido in ActiveMeidoList)
             {
                 meido.UpdateMeido -= OnUpdateMeido;
+                meido.GravityMove -= OnGravityMove;
                 meido.Unload();
             }
             ActiveMeidoList.Clear();
@@ -193,6 +215,17 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             foreach (Meido meido in ActiveMeidoList)
             {
                 meido.UpdateMeido += OnUpdateMeido;
+                meido.GravityMove += OnGravityMove;
+            }
+        }
+
+        private void OnGravityMove(object sender, GravityEventArgs args)
+        {
+            if (!GlobalGravity) return;
+
+            foreach (Meido meido in ActiveMeidoList)
+            {
+                meido.ApplyGravity(args.LocalPosition, args.IsSkirt);
             }
         }
     }

+ 152 - 1
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Meido/Meido.cs

@@ -131,6 +131,13 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                 OnUpdateMeido();
             }
         }
+        private DragPointGravity hairGravityDragPoint;
+        private GravityTransformControl hairGravityControl;
+        private DragPointGravity skirtGravityDragPoint;
+        private GravityTransformControl skirtGravityControl;
+        public bool HairGravityValid => hairGravityControl != null;
+        public bool SkirtGravityValid => skirtGravityControl != null;
+        public event EventHandler<GravityEventArgs> GravityMove;
         public float[] BlendValuesBackup { get; private set; }
         public float[] BlendValues { get; private set; }
         public Quaternion DefaultEyeRotL { get; private set; }
@@ -187,6 +194,12 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 
                 Body.quaDefEyeL = DefaultEyeRotL;
                 Body.quaDefEyeR = DefaultEyeRotR;
+
+                HairGravityActive = false;
+                SkirtGravityActive = false;
+
+                if (HairGravityValid) hairGravityDragPoint.Move -= OnGravityEvent;
+                if (SkirtGravityValid) skirtGravityDragPoint.Move -= OnGravityEvent;
             }
 
             Body.MuneYureL(1f);
@@ -204,6 +217,9 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         {
             Unload();
 
+            DestroyGravityControl(ref hairGravityControl);
+            DestroyGravityControl(ref skirtGravityControl);
+
             Maid.SetPos(Vector3.zero);
             Maid.SetRot(Vector3.zero);
             Maid.SetPosOffset(Vector3.zero);
@@ -371,6 +387,8 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                 Maid.ResetProp(name[1]);
             }
             Maid.AllProcProp();
+            hairGravityControl?.OnChangeMekure();
+            skirtGravityControl?.OnChangeMekure();
         }
 
         public void SetMpnProp(MpnAttachProp prop, bool detach)
@@ -387,6 +405,12 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             Maid.AllProcProp();
         }
 
+        public void ApplyGravity(Vector3 position, bool skirt = false)
+        {
+            DragPointGravity dragPoint = skirt ? skirtGravityDragPoint : hairGravityDragPoint;
+            if (dragPoint != null) dragPoint.MyObject.localPosition = position;
+        }
+
         private CacheBoneDataArray GetCacheBoneData()
         {
             CacheBoneDataArray cache = this.Maid.gameObject.GetComponent<CacheBoneDataArray>();
@@ -407,9 +431,15 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                 BlendValues = Utility.GetFieldValue<TMorph, float[]>(faceMorph, "BlendValues");
                 DefaultEyeRotL = Body.quaDefEyeL;
                 DefaultEyeRotR = Body.quaDefEyeR;
+
+                InitializeGravityControls();
+
                 initialized = true;
             }
 
+            if (HairGravityValid) hairGravityDragPoint.Move += OnGravityEvent;
+            if (SkirtGravityValid) skirtGravityDragPoint.Move += OnGravityEvent;
+
             IKManager.Initialize();
 
             IK = true;
@@ -417,12 +447,121 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             Bone = false;
             Loading = false;
         }
+        public bool HairGravityActive
+        {
+            get => HairGravityValid && hairGravityDragPoint.gameObject.activeSelf;
+            set
+            {
+                if (HairGravityValid && value != HairGravityActive)
+                {
+                    hairGravityDragPoint.gameObject.SetActive(value);
+                    hairGravityControl.isEnabled = value;
+                }
+            }
+        }
+        public bool SkirtGravityActive
+        {
+            get => SkirtGravityValid && skirtGravityDragPoint.gameObject.activeSelf;
+            set
+            {
+                if (SkirtGravityValid && value != SkirtGravityActive)
+                {
+                    skirtGravityDragPoint.gameObject.SetActive(value);
+                    skirtGravityControl.isEnabled = value;
+                }
+            }
+        }
+        private void InitializeGravityControls()
+        {
+            hairGravityControl = InitializeGravityControl("hair");
+            if (hairGravityControl.isValid)
+            {
+                hairGravityDragPoint = MakeGravityDragPoint(hairGravityControl);
+                HairGravityActive = false;
+            }
+            else DestroyGravityControl(ref hairGravityControl);
+
+            skirtGravityControl = InitializeGravityControl("skirt");
+            if (skirtGravityControl.isValid)
+            {
+                skirtGravityDragPoint = MakeGravityDragPoint(skirtGravityControl);
+                SkirtGravityActive = false;
+            }
+            else DestroyGravityControl(ref skirtGravityControl);
+        }
 
-        public void OnUpdateMeido(MeidoUpdateEventArgs args = null)
+        private DragPointGravity MakeGravityDragPoint(GravityTransformControl control)
+        {
+            DragPointGravity gravityDragpoint = DragPoint.Make<DragPointGravity>(
+                PrimitiveType.Cube, Vector3.one * 0.12f, DragPoint.LightBlue
+            );
+            gravityDragpoint.Initialize(() => control.transform.position, () => Vector3.zero);
+            gravityDragpoint.Set(control.transform);
+
+            return gravityDragpoint;
+        }
+
+        private GravityTransformControl InitializeGravityControl(string category)
+        {
+            Transform bone = Body.GetBone("Bip01");
+            string gravityGoName = $"GravityDatas_{Maid.status.guid}_{category}";
+            Transform gravityTransform = Maid.gameObject.transform.Find(gravityGoName);
+            if (gravityTransform == null)
+            {
+                GameObject go = new GameObject();
+                go.name = gravityGoName;
+                go.transform.SetParent(bone, false);
+                go.transform.SetParent(Maid.transform, true);
+                go.transform.localScale = Vector3.one;
+                go.transform.rotation = Quaternion.identity;
+                GameObject go2 = new GameObject();
+                go2.transform.SetParent(go.transform, false);
+                go2.name = gravityGoName;
+                gravityTransform = go2.transform;
+            }
+            else
+            {
+                gravityTransform = gravityTransform.GetChild(0);
+                GravityTransformControl control = gravityTransform.GetComponent<GravityTransformControl>();
+                if (control != null) GameObject.Destroy(control);
+            }
+
+            GravityTransformControl gravityControl = gravityTransform.gameObject.AddComponent<GravityTransformControl>();
+
+            SlotID[] slots = category == "skirt"
+                ? new[] { SlotID.skirt, SlotID.onepiece, SlotID.mizugi, SlotID.panz }
+                : new[] { SlotID.hairF, SlotID.hairR, SlotID.hairS, SlotID.hairT };
+
+            gravityControl.SetTargetSlods(slots);
+            gravityControl.forceRate = 0.1f;
+
+            return gravityControl;
+        }
+
+        private void DestroyGravityControl(ref GravityTransformControl control)
+        {
+            if (control != null)
+            {
+                GameObject.Destroy(control.transform.parent.gameObject);
+                control = null;
+            }
+        }
+
+        private void OnUpdateMeido(MeidoUpdateEventArgs args = null)
         {
             this.UpdateMeido?.Invoke(this, args ?? MeidoUpdateEventArgs.Empty);
         }
 
+        private void OnGravityEvent(object sender, EventArgs args) => OnGravityChange((DragPointGravity)sender);
+
+        private void OnGravityChange(DragPointGravity dragPoint)
+        {
+            GravityEventArgs args = new GravityEventArgs(
+                dragPoint == skirtGravityDragPoint, dragPoint.MyObject.transform.localPosition
+            );
+            this.GravityMove?.Invoke(this, args);
+        }
+
         public void Serialize(BinaryWriter binaryWriter)
         {
             using (MemoryStream memoryStream = new MemoryStream())
@@ -667,6 +806,18 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         }
     }
 
+    public class GravityEventArgs : EventArgs
+    {
+        public Vector3 LocalPosition { get; }
+        public bool IsSkirt { get; }
+
+        public GravityEventArgs(bool isSkirt, Vector3 localPosition)
+        {
+            this.LocalPosition = localPosition;
+            this.IsSkirt = isSkirt;
+        }
+    }
+
     public struct PoseInfo
     {
         public string PoseGroup { get; }