Meido.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. using System;
  2. using System.IO;
  3. using System.Collections;
  4. using System.Linq;
  5. using System.Xml.Linq;
  6. using UnityEngine;
  7. using static TBody;
  8. using System.Collections.Generic;
  9. namespace COM3D2.MeidoPhotoStudio.Plugin
  10. {
  11. internal class Meido : ISerializable
  12. {
  13. private bool initialized;
  14. private DragPointGravity hairGravityDragPoint;
  15. private GravityTransformControl hairGravityControl;
  16. public bool HairGravityValid => hairGravityControl != null;
  17. private DragPointGravity skirtGravityDragPoint;
  18. private GravityTransformControl skirtGravityControl;
  19. public bool SkirtGravityValid => skirtGravityControl != null;
  20. private float[] BlendSetValueBackup;
  21. public const int meidoDataVersion = 1000;
  22. public static readonly PoseInfo DefaultPose =
  23. new PoseInfo(Constants.PoseGroupList[0], Constants.PoseDict[Constants.PoseGroupList[0]][0]);
  24. public static readonly string defaultFaceBlendSet = "通常";
  25. public static readonly string[] faceKeys = new string[24]
  26. {
  27. "eyeclose", "eyeclose2", "eyeclose3", "eyebig", "eyeclose6", "eyeclose5", "hitomih",
  28. "hitomis", "mayuha", "mayuw", "mayuup", "mayuv", "mayuvhalf", "moutha", "mouths",
  29. "mouthc", "mouthi", "mouthup", "mouthdw", "mouthhe", "mouthuphalf", "tangout",
  30. "tangup", "tangopen"
  31. };
  32. public static readonly string[] faceToggleKeys = new string[12]
  33. {
  34. // blush, shade, nose up, tears, drool, teeth
  35. "hoho2", "shock", "nosefook", "namida", "yodare", "toothoff",
  36. // cry 1, cry 2, cry 3, blush 1, blush 2, blush 3
  37. "tear1", "tear2", "tear3", "hohos", "hoho", "hohol"
  38. };
  39. public enum Curl
  40. {
  41. front, back, shift
  42. }
  43. public event EventHandler<MeidoUpdateEventArgs> UpdateMeido;
  44. public int StockNo { get; }
  45. public Maid Maid { get; }
  46. public TBody Body => Maid.body0;
  47. public MeidoDragPointManager IKManager { get; }
  48. public Texture2D Portrait { get; }
  49. public PoseInfo CachedPose { get; private set; } = DefaultPose;
  50. public string CurrentFaceBlendSet { get; private set; } = defaultFaceBlendSet;
  51. public int Slot { get; private set; }
  52. public bool Loading { get; private set; }
  53. public string FirstName => Maid.status.firstName;
  54. public string LastName => Maid.status.lastName;
  55. public bool Busy => Maid.IsBusy && Loading;
  56. public bool CurlingFront => Maid.IsItemChange("skirt", "めくれスカート")
  57. || Maid.IsItemChange("onepiece", "めくれスカート");
  58. public bool CurlingBack => Maid.IsItemChange("skirt", "めくれスカート後ろ")
  59. || Maid.IsItemChange("onepiece", "めくれスカート後ろ");
  60. public bool PantsuShift => Maid.IsItemChange("panz", "パンツずらし")
  61. || Maid.IsItemChange("mizugi", "パンツずらし");
  62. private bool freeLook;
  63. public bool FreeLook
  64. {
  65. get => freeLook;
  66. set
  67. {
  68. if (freeLook == value) return;
  69. freeLook = value;
  70. Body.trsLookTarget = freeLook ? null : GameMain.Instance.MainCamera.transform;
  71. OnUpdateMeido();
  72. }
  73. }
  74. public bool HeadToCam
  75. {
  76. get => Body.isLoadedBody && Body.boHeadToCam;
  77. set
  78. {
  79. if (!Body.isLoadedBody || HeadToCam == value) return;
  80. Body.HeadToCamPer = 0f;
  81. Body.boHeadToCam = value;
  82. if (!HeadToCam && !EyeToCam) FreeLook = false;
  83. OnUpdateMeido();
  84. }
  85. }
  86. public bool EyeToCam
  87. {
  88. get => Body.isLoadedBody && Body.boEyeToCam;
  89. set
  90. {
  91. if (!Body.isLoadedBody || EyeToCam == value) return;
  92. Body.boEyeToCam = value;
  93. if (!HeadToCam && !EyeToCam) FreeLook = false;
  94. OnUpdateMeido();
  95. }
  96. }
  97. public bool Stop
  98. {
  99. get => !Body.isLoadedBody || !Maid.GetAnimation().isPlaying;
  100. set
  101. {
  102. if (!Body.isLoadedBody || value == Stop) return;
  103. if (value) Maid.GetAnimation().Stop();
  104. else
  105. {
  106. Body.boEyeToCam = true;
  107. Body.boHeadToCam = true;
  108. SetPose(CachedPose.Pose);
  109. }
  110. OnUpdateMeido();
  111. }
  112. }
  113. public bool IK
  114. {
  115. get => IKManager.Active;
  116. set
  117. {
  118. if (value == IKManager.Active) return;
  119. IKManager.Active = value;
  120. }
  121. }
  122. public bool Bone
  123. {
  124. get => IKManager.IsBone;
  125. set
  126. {
  127. if (value == Bone) return;
  128. IKManager.IsBone = value;
  129. OnUpdateMeido();
  130. }
  131. }
  132. public bool HairGravityActive
  133. {
  134. get => HairGravityValid && hairGravityDragPoint.gameObject.activeSelf;
  135. set
  136. {
  137. if (HairGravityValid && value != HairGravityActive)
  138. {
  139. hairGravityDragPoint.gameObject.SetActive(value);
  140. hairGravityControl.isEnabled = value;
  141. }
  142. }
  143. }
  144. public bool SkirtGravityActive
  145. {
  146. get => SkirtGravityValid && skirtGravityDragPoint.gameObject.activeSelf;
  147. set
  148. {
  149. if (SkirtGravityValid && value != SkirtGravityActive)
  150. {
  151. skirtGravityDragPoint.gameObject.SetActive(value);
  152. skirtGravityControl.isEnabled = value;
  153. }
  154. }
  155. }
  156. public event EventHandler<GravityEventArgs> GravityMove;
  157. public Quaternion DefaultEyeRotL { get; private set; }
  158. public Quaternion DefaultEyeRotR { get; private set; }
  159. public Meido(int stockMaidIndex)
  160. {
  161. StockNo = stockMaidIndex;
  162. Maid = GameMain.Instance.CharacterMgr.GetStockMaid(stockMaidIndex);
  163. Portrait = Maid.GetThumIcon();
  164. IKManager = new MeidoDragPointManager(this);
  165. IKManager.SelectMaid += (s, args) => OnUpdateMeido(args);
  166. }
  167. public void BeginLoad()
  168. {
  169. FreeLook = false;
  170. Maid.Visible = true;
  171. Body.boHeadToCam = true;
  172. Body.boEyeToCam = true;
  173. Body.SetBoneHitHeightY(-1000f);
  174. }
  175. public void Load(int slot)
  176. {
  177. Slot = slot;
  178. Loading = true;
  179. if (!Body.isLoadedBody)
  180. {
  181. Maid.DutPropAll();
  182. Maid.AllProcPropSeqStart();
  183. }
  184. GameMain.Instance.StartCoroutine(Load());
  185. }
  186. private IEnumerator Load()
  187. {
  188. while (Maid.IsBusy) yield return null;
  189. yield return new WaitForEndOfFrame();
  190. OnBodyLoad();
  191. }
  192. public void Unload()
  193. {
  194. if (Body.isLoadedBody)
  195. {
  196. DetachAllMpnAttach();
  197. Body.jbMuneL.enabled = true;
  198. Body.jbMuneR.enabled = true;
  199. Body.quaDefEyeL = DefaultEyeRotL;
  200. Body.quaDefEyeR = DefaultEyeRotR;
  201. HairGravityActive = false;
  202. SkirtGravityActive = false;
  203. if (HairGravityValid)
  204. {
  205. hairGravityDragPoint.Move -= OnGravityEvent;
  206. ApplyGravity(Vector3.zero, skirt: false);
  207. }
  208. if (SkirtGravityValid)
  209. {
  210. skirtGravityDragPoint.Move -= OnGravityEvent;
  211. ApplyGravity(Vector3.zero, skirt: true);
  212. }
  213. }
  214. Body.MuneYureL(1f);
  215. Body.MuneYureR(1f);
  216. Body.SetMaskMode(MaskMode.None);
  217. Body.SetBoneHitHeightY(0f);
  218. Maid.Visible = false;
  219. IKManager.Destroy();
  220. }
  221. public void Deactivate()
  222. {
  223. if (Body.isLoadedBody) SetFaceBlendSet(defaultFaceBlendSet);
  224. Unload();
  225. DestroyGravityControl(ref hairGravityControl);
  226. DestroyGravityControl(ref skirtGravityControl);
  227. GameObject.Destroy(hairGravityDragPoint?.gameObject);
  228. GameObject.Destroy(skirtGravityDragPoint?.gameObject);
  229. Maid.SetPos(Vector3.zero);
  230. Maid.SetRot(Vector3.zero);
  231. Maid.SetPosOffset(Vector3.zero);
  232. Body.transform.localScale = Vector3.one;
  233. Maid.ResetAll();
  234. Maid.MabatakiUpdateStop = false;
  235. Maid.ActiveSlotNo = -1;
  236. }
  237. public void SetPose(PoseInfo poseInfo)
  238. {
  239. CachedPose = poseInfo;
  240. SetPose(poseInfo.Pose);
  241. }
  242. public void SetPose(string pose)
  243. {
  244. if (!Body.isLoadedBody) return;
  245. if (pose.StartsWith(Constants.customPosePath))
  246. {
  247. byte[] poseBuffer = File.ReadAllBytes(pose);
  248. string hash = Path.GetFileName(pose).GetHashCode().ToString();
  249. Body.CrossFade(hash, poseBuffer, loop: true, fade: 0f);
  250. }
  251. else
  252. {
  253. string[] poseComponents = pose.Split(',');
  254. pose = poseComponents[0] + ".anm";
  255. Maid.CrossFade(pose, loop: true, val: 0f);
  256. Maid.GetAnimation().Play();
  257. if (poseComponents.Length > 1)
  258. {
  259. Maid.GetAnimation()[pose].time = float.Parse(poseComponents[1]);
  260. Maid.GetAnimation()[pose].speed = 0f;
  261. }
  262. }
  263. Maid.SetAutoTwistAll(true);
  264. SetMune();
  265. }
  266. public void CopyPose(Meido fromMeido)
  267. {
  268. byte[] poseBinary = fromMeido.SerializePose();
  269. string tag = $"copy_{fromMeido.Maid.status.guid}";
  270. Body.CrossFade(tag, poseBinary, false, true, false, 0f);
  271. Maid.SetAutoTwistAll(true);
  272. Maid.transform.rotation = fromMeido.Maid.transform.rotation;
  273. SetMune();
  274. }
  275. public void SetMune(bool drag = false)
  276. {
  277. bool momiOrPaizuri = CachedPose.Pose.Contains("_momi") || CachedPose.Pose.Contains("paizuri_");
  278. float onL = (drag || momiOrPaizuri) ? 0f : 1f;
  279. Body.MuneYureL(onL);
  280. Body.MuneYureR(onL);
  281. Body.jbMuneL.enabled = !drag;
  282. Body.jbMuneR.enabled = !drag;
  283. }
  284. public void SetHandPreset(string filename, bool right)
  285. {
  286. XDocument handDocument = XDocument.Load(filename);
  287. XElement handElement = handDocument.Element("FingerData");
  288. if (handElement.IsEmpty || handElement.Element("GameVersion").IsEmpty
  289. || handElement.Element("RightData").IsEmpty || handElement.Element("BinaryData").IsEmpty
  290. ) return;
  291. Stop = true;
  292. bool rightData = bool.Parse(handElement.Element("RightData").Value);
  293. string base64Data = handElement.Element("BinaryData").Value;
  294. byte[] handData = Convert.FromBase64String(base64Data);
  295. IKManager.DeserializeHand(handData, right, rightData != right);
  296. }
  297. public byte[] SerializePose(bool frameBinary = false)
  298. {
  299. CacheBoneDataArray cache = GetCacheBoneData();
  300. return frameBinary ? cache.GetFrameBinary(true, true) : cache.GetAnmBinary(true, true);
  301. }
  302. public Dictionary<string, float> SerializeFace()
  303. {
  304. Dictionary<string, float> faceData = new Dictionary<string, float>();
  305. foreach (string hash in faceKeys.Concat(faceToggleKeys))
  306. {
  307. try
  308. {
  309. float value = GetFaceBlendValue(hash);
  310. faceData.Add(hash, value);
  311. }
  312. catch { }
  313. }
  314. return faceData;
  315. }
  316. public void SetFaceBlendSet(string blendSet, bool custom = false)
  317. {
  318. if (custom)
  319. {
  320. XDocument faceDocument = XDocument.Load(blendSet);
  321. XElement faceDataElement = faceDocument.Element("FaceData");
  322. if (faceDataElement.IsEmpty) return;
  323. HashSet<string> hashKeys = new HashSet<string>(faceKeys.Concat(faceToggleKeys));
  324. foreach (XElement element in faceDataElement.Elements())
  325. {
  326. string key;
  327. if ((key = (string)element.Attribute("name")) != null && !hashKeys.Contains(key)) continue;
  328. if (float.TryParse(element.Value, out float value))
  329. {
  330. try
  331. {
  332. SetFaceBlendValue(key, value);
  333. }
  334. catch { }
  335. }
  336. }
  337. }
  338. else
  339. {
  340. ApplyBackupBlendSet();
  341. CurrentFaceBlendSet = blendSet;
  342. BackupBlendSetValuess();
  343. Maid.FaceAnime(blendSet, 0f);
  344. }
  345. StopBlink();
  346. OnUpdateMeido();
  347. }
  348. public void SetFaceBlendValue(string hash, float value)
  349. {
  350. TMorph morph = Body.Face.morph;
  351. if (hash == "nosefook") Maid.boNoseFook = morph.boNoseFook = value > 0f;
  352. else
  353. {
  354. hash = Utility.GP01FbFaceHash(morph, hash);
  355. try
  356. {
  357. morph.dicBlendSet[CurrentFaceBlendSet][(int)morph.hash[hash]] = value;
  358. }
  359. catch { }
  360. }
  361. }
  362. public float GetFaceBlendValue(string hash)
  363. {
  364. TMorph morph = Body.Face.morph;
  365. if (hash == "nosefook") return (Maid.boNoseFook || morph.boNoseFook) ? 1f : 0f;
  366. hash = Utility.GP01FbFaceHash(morph, hash);
  367. return morph.dicBlendSet[CurrentFaceBlendSet][(int)morph.hash[hash]];
  368. }
  369. public void StopBlink()
  370. {
  371. Maid.MabatakiUpdateStop = true;
  372. Body.Face.morph.EyeMabataki = 0f;
  373. Utility.SetFieldValue(Maid, "MabatakiVal", 0f);
  374. }
  375. public void SetMaskMode(MaskMode maskMode)
  376. {
  377. bool invisibleBody = !Body.GetMask(SlotID.body);
  378. Body.SetMaskMode(maskMode);
  379. if (invisibleBody) SetBodyMask(false);
  380. }
  381. public void SetBodyMask(bool enabled)
  382. {
  383. Hashtable table = Utility.GetFieldValue<TBody, Hashtable>(Body, "m_hFoceHide");
  384. foreach (SlotID bodySlot in MaidDressingPane.bodySlots)
  385. {
  386. table[bodySlot] = enabled;
  387. }
  388. if (Body.goSlot[19].m_strModelFileName.Contains("melala_body"))
  389. {
  390. table[SlotID.accHana] = enabled;
  391. }
  392. Body.FixMaskFlag();
  393. Body.FixVisibleFlag(false);
  394. }
  395. public void SetCurling(Curl curling, bool enabled)
  396. {
  397. string[] name = curling == Curl.shift
  398. ? new[] { "panz", "mizugi" }
  399. : new[] { "skirt", "onepiece" };
  400. if (enabled)
  401. {
  402. string action = curling == Curl.shift
  403. ? "パンツずらし" : curling == Curl.front
  404. ? "めくれスカート" : "めくれスカート後ろ";
  405. Maid.ItemChangeTemp(name[0], action);
  406. Maid.ItemChangeTemp(name[1], action);
  407. }
  408. else
  409. {
  410. Maid.ResetProp(name[0]);
  411. Maid.ResetProp(name[1]);
  412. }
  413. Maid.AllProcProp();
  414. hairGravityControl?.OnChangeMekure();
  415. skirtGravityControl?.OnChangeMekure();
  416. }
  417. public void SetMpnProp(MpnAttachProp prop, bool detach)
  418. {
  419. if (detach) Maid.ResetProp(prop.Tag, false);
  420. else Maid.SetProp(prop.Tag, prop.MenuFile, 0, true);
  421. Maid.AllProcProp();
  422. }
  423. public void DetachAllMpnAttach()
  424. {
  425. Maid.ResetProp(MPN.kousoku_lower, false);
  426. Maid.ResetProp(MPN.kousoku_upper, false);
  427. Maid.AllProcProp();
  428. }
  429. public void ApplyGravity(Vector3 position, bool skirt = false)
  430. {
  431. GravityTransformControl control = skirt ? skirtGravityControl : hairGravityControl;
  432. if (control != null) control.transform.localPosition = position;
  433. }
  434. private void BackupBlendSetValuess()
  435. {
  436. float[] values = Body.Face.morph.dicBlendSet[CurrentFaceBlendSet];
  437. BlendSetValueBackup = new float[values.Length];
  438. values.CopyTo(BlendSetValueBackup, 0);
  439. }
  440. private void ApplyBackupBlendSet()
  441. {
  442. BlendSetValueBackup.CopyTo(Body.Face.morph.dicBlendSet[CurrentFaceBlendSet], 0);
  443. Maid.boNoseFook = false;
  444. }
  445. private CacheBoneDataArray GetCacheBoneData()
  446. {
  447. CacheBoneDataArray cache = Maid.gameObject.GetComponent<CacheBoneDataArray>();
  448. if (cache == null)
  449. {
  450. cache = Maid.gameObject.AddComponent<CacheBoneDataArray>();
  451. cache.CreateCache(Body.GetBone("Bip01"));
  452. }
  453. return cache;
  454. }
  455. private void OnBodyLoad()
  456. {
  457. if (!initialized)
  458. {
  459. TMorph faceMorph = Body.Face.morph;
  460. DefaultEyeRotL = Body.quaDefEyeL;
  461. DefaultEyeRotR = Body.quaDefEyeR;
  462. InitializeGravityControls();
  463. initialized = true;
  464. }
  465. if (BlendSetValueBackup == null) BackupBlendSetValuess();
  466. if (HairGravityValid) hairGravityDragPoint.Move += OnGravityEvent;
  467. if (SkirtGravityValid) skirtGravityDragPoint.Move += OnGravityEvent;
  468. IKManager.Initialize();
  469. IK = true;
  470. Stop = false;
  471. Bone = false;
  472. Loading = false;
  473. }
  474. private void InitializeGravityControls()
  475. {
  476. hairGravityControl = InitializeGravityControl("hair");
  477. if (hairGravityControl.isValid)
  478. {
  479. hairGravityDragPoint = MakeGravityDragPoint(hairGravityControl);
  480. HairGravityActive = false;
  481. }
  482. else DestroyGravityControl(ref hairGravityControl);
  483. skirtGravityControl = InitializeGravityControl("skirt");
  484. if (skirtGravityControl.isValid)
  485. {
  486. skirtGravityDragPoint = MakeGravityDragPoint(skirtGravityControl);
  487. SkirtGravityActive = false;
  488. }
  489. else DestroyGravityControl(ref skirtGravityControl);
  490. }
  491. private DragPointGravity MakeGravityDragPoint(GravityTransformControl control)
  492. {
  493. DragPointGravity gravityDragpoint = DragPoint.Make<DragPointGravity>(
  494. PrimitiveType.Cube, Vector3.one * 0.12f
  495. );
  496. gravityDragpoint.Initialize(() => control.transform.position, () => Vector3.zero);
  497. gravityDragpoint.Set(control.transform);
  498. return gravityDragpoint;
  499. }
  500. private GravityTransformControl InitializeGravityControl(string category)
  501. {
  502. Transform bone = Body.GetBone("Bip01");
  503. string gravityGoName = $"GravityDatas_{Maid.status.guid}_{category}";
  504. Transform gravityTransform = Maid.gameObject.transform.Find(gravityGoName);
  505. if (gravityTransform == null)
  506. {
  507. GameObject go = new GameObject(gravityGoName);
  508. go.transform.SetParent(bone, false);
  509. go.transform.SetParent(Maid.transform, true);
  510. go.transform.localScale = Vector3.one;
  511. go.transform.rotation = Quaternion.identity;
  512. GameObject go2 = new GameObject(gravityGoName);
  513. go2.transform.SetParent(go.transform, false);
  514. gravityTransform = go2.transform;
  515. }
  516. else
  517. {
  518. gravityTransform = gravityTransform.GetChild(0);
  519. GravityTransformControl control = gravityTransform.GetComponent<GravityTransformControl>();
  520. if (control != null) GameObject.Destroy(control);
  521. }
  522. GravityTransformControl gravityControl = gravityTransform.gameObject.AddComponent<GravityTransformControl>();
  523. SlotID[] slots = category == "skirt"
  524. ? new[] { SlotID.skirt, SlotID.onepiece, SlotID.mizugi, SlotID.panz }
  525. : new[] { SlotID.hairF, SlotID.hairR, SlotID.hairS, SlotID.hairT };
  526. gravityControl.SetTargetSlods(slots);
  527. gravityControl.forceRate = 0.1f;
  528. return gravityControl;
  529. }
  530. private void DestroyGravityControl(ref GravityTransformControl control)
  531. {
  532. if (control != null)
  533. {
  534. if (control.isValid)
  535. {
  536. control.transform.localPosition = Vector3.zero;
  537. control.Update();
  538. }
  539. GameObject.Destroy(control.transform.parent.gameObject);
  540. control = null;
  541. }
  542. }
  543. private void OnUpdateMeido(MeidoUpdateEventArgs args = null)
  544. {
  545. UpdateMeido?.Invoke(this, args ?? MeidoUpdateEventArgs.Empty);
  546. }
  547. private void OnGravityEvent(object sender, EventArgs args) => OnGravityChange((DragPointGravity)sender);
  548. private void OnGravityChange(DragPointGravity dragPoint)
  549. {
  550. GravityEventArgs args = new GravityEventArgs(
  551. dragPoint == skirtGravityDragPoint, dragPoint.MyObject.transform.localPosition
  552. );
  553. GravityMove?.Invoke(this, args);
  554. }
  555. public void Serialize(BinaryWriter binaryWriter)
  556. {
  557. using (MemoryStream memoryStream = new MemoryStream())
  558. using (BinaryWriter tempWriter = new BinaryWriter(memoryStream))
  559. {
  560. // transform
  561. tempWriter.WriteVector3(Maid.transform.position);
  562. tempWriter.WriteQuaternion(Maid.transform.rotation);
  563. tempWriter.WriteVector3(Maid.transform.localScale);
  564. // pose
  565. byte[] poseBuffer = SerializePose(true);
  566. tempWriter.Write(poseBuffer.Length);
  567. tempWriter.Write(poseBuffer);
  568. CachedPose.Serialize(tempWriter);
  569. // eye direction
  570. tempWriter.WriteQuaternion(Body.quaDefEyeL * Quaternion.Inverse(DefaultEyeRotL));
  571. tempWriter.WriteQuaternion(Body.quaDefEyeR * Quaternion.Inverse(DefaultEyeRotR));
  572. // free look
  573. tempWriter.Write(FreeLook);
  574. if (FreeLook)
  575. {
  576. tempWriter.WriteVector3(Body.offsetLookTarget);
  577. tempWriter.WriteVector3(Utility.GetFieldValue<TBody, Vector3>(Body, "HeadEulerAngle"));
  578. }
  579. // Head/eye to camera
  580. tempWriter.Write(HeadToCam);
  581. tempWriter.Write(EyeToCam);
  582. // face
  583. SerializeFace(tempWriter);
  584. // body visible
  585. tempWriter.Write(Body.GetMask(SlotID.body));
  586. // clothing
  587. foreach (SlotID clothingSlot in MaidDressingPane.clothingSlots)
  588. {
  589. bool value = true;
  590. if (clothingSlot == SlotID.wear)
  591. {
  592. if (MaidDressingPane.wearSlots.Any(slot => Body.GetSlotLoaded(slot)))
  593. {
  594. value = MaidDressingPane.wearSlots.Any(slot => Body.GetMask(slot));
  595. }
  596. }
  597. else if (clothingSlot == SlotID.megane)
  598. {
  599. SlotID[] slots = new[] { SlotID.megane, SlotID.accHead };
  600. if (slots.Any(slot => Body.GetSlotLoaded(slot)))
  601. {
  602. value = slots.Any(slot => Body.GetMask(slot));
  603. }
  604. }
  605. else if (Body.GetSlotLoaded(clothingSlot))
  606. {
  607. value = Body.GetMask(clothingSlot);
  608. }
  609. tempWriter.Write(value);
  610. }
  611. // hair/skirt gravity
  612. tempWriter.Write(HairGravityActive);
  613. if (HairGravityActive) tempWriter.WriteVector3(hairGravityControl.transform.localPosition);
  614. tempWriter.Write(SkirtGravityActive);
  615. if (SkirtGravityActive) tempWriter.WriteVector3(skirtGravityControl.transform.localPosition);
  616. // zurashi and mekure
  617. tempWriter.Write(CurlingFront);
  618. tempWriter.Write(CurlingBack);
  619. tempWriter.Write(PantsuShift);
  620. bool hasKousokuUpper = Body.GetSlotLoaded(SlotID.kousoku_upper);
  621. tempWriter.Write(hasKousokuUpper);
  622. if (hasKousokuUpper) tempWriter.Write(Maid.GetProp(MPN.kousoku_upper).strTempFileName);
  623. bool hasKousokuLower = Body.GetSlotLoaded(SlotID.kousoku_lower);
  624. tempWriter.Write(hasKousokuLower);
  625. if (hasKousokuLower) tempWriter.Write(Maid.GetProp(MPN.kousoku_lower).strTempFileName);
  626. binaryWriter.Write(memoryStream.Length);
  627. binaryWriter.Write(memoryStream.ToArray());
  628. }
  629. }
  630. private void SerializeFace(BinaryWriter binaryWriter)
  631. {
  632. binaryWriter.Write("MPS_FACE");
  633. foreach (string hash in faceKeys.Concat(faceToggleKeys))
  634. {
  635. try
  636. {
  637. float value = GetFaceBlendValue(hash);
  638. binaryWriter.Write(hash);
  639. binaryWriter.Write(value);
  640. }
  641. catch { }
  642. }
  643. binaryWriter.Write("END_FACE");
  644. }
  645. public void Deserialize(BinaryReader binaryReader) => Deserialize(binaryReader, meidoDataVersion, false);
  646. public void Deserialize(BinaryReader binaryReader, int dataVersion, bool mmScene)
  647. {
  648. Maid.GetAnimation().Stop();
  649. DetachAllMpnAttach();
  650. binaryReader.ReadInt64(); // meido buffer length
  651. // transform
  652. Maid.transform.position = binaryReader.ReadVector3();
  653. Maid.transform.rotation = binaryReader.ReadQuaternion();
  654. Maid.transform.localScale = binaryReader.ReadVector3();
  655. // pose
  656. if (mmScene) IKManager.Deserialize(binaryReader);
  657. else
  658. {
  659. int poseBufferLength = binaryReader.ReadInt32();
  660. byte[] poseBuffer = binaryReader.ReadBytes(poseBufferLength);
  661. GetCacheBoneData().SetFrameBinary(poseBuffer);
  662. }
  663. Body.MuneYureL(0f);
  664. Body.MuneYureR(0f);
  665. Body.jbMuneL.enabled = false;
  666. Body.jbMuneR.enabled = false;
  667. CachedPose = PoseInfo.Deserialize(binaryReader);
  668. // eye direction
  669. Body.quaDefEyeL = binaryReader.ReadQuaternion() * DefaultEyeRotL;
  670. Body.quaDefEyeR = binaryReader.ReadQuaternion() * DefaultEyeRotR;
  671. // free look
  672. FreeLook = binaryReader.ReadBoolean();
  673. if (FreeLook)
  674. {
  675. Body.offsetLookTarget = binaryReader.ReadVector3();
  676. // Head angle cannot be resolved with just the offsetLookTarget
  677. if (!mmScene)
  678. {
  679. Utility.SetFieldValue(Body, "HeadEulerAngleG", Vector3.zero);
  680. Utility.SetFieldValue(Body, "HeadEulerAngle", binaryReader.ReadVector3());
  681. }
  682. }
  683. // Head/eye to camera
  684. HeadToCam = binaryReader.ReadBoolean();
  685. EyeToCam = binaryReader.ReadBoolean();
  686. // face
  687. DeserializeFace(binaryReader);
  688. // body visible
  689. SetBodyMask(binaryReader.ReadBoolean());
  690. // clothing
  691. foreach (SlotID clothingSlot in MaidDressingPane.clothingSlots)
  692. {
  693. bool value = binaryReader.ReadBoolean();
  694. if (mmScene) continue;
  695. if (clothingSlot == SlotID.wear)
  696. {
  697. Body.SetMask(SlotID.wear, value);
  698. Body.SetMask(SlotID.mizugi, value);
  699. Body.SetMask(SlotID.onepiece, value);
  700. }
  701. else if (clothingSlot == SlotID.megane)
  702. {
  703. Body.SetMask(SlotID.megane, value);
  704. Body.SetMask(SlotID.accHead, value);
  705. }
  706. else if (Body.GetSlotLoaded(clothingSlot))
  707. {
  708. Body.SetMask(clothingSlot, value);
  709. }
  710. }
  711. // hair/skirt gravity
  712. bool hairGravityActive = binaryReader.ReadBoolean();
  713. if (hairGravityActive)
  714. {
  715. HairGravityActive = true;
  716. ApplyGravity(binaryReader.ReadVector3(), skirt: false);
  717. }
  718. bool skirtGravityActive = binaryReader.ReadBoolean();
  719. if (skirtGravityActive)
  720. {
  721. SkirtGravityActive = true;
  722. ApplyGravity(binaryReader.ReadVector3(), skirt: true);
  723. }
  724. // zurashi and mekure
  725. bool curlingFront = binaryReader.ReadBoolean();
  726. bool curlingBack = binaryReader.ReadBoolean();
  727. bool curlingPantsu = binaryReader.ReadBoolean();
  728. if (!mmScene)
  729. {
  730. if (CurlingFront != curlingFront) SetCurling(Curl.front, curlingFront);
  731. if (CurlingBack != curlingBack) SetCurling(Curl.back, curlingBack);
  732. SetCurling(Curl.shift, curlingPantsu);
  733. }
  734. bool hasKousokuUpper = binaryReader.ReadBoolean();
  735. if (hasKousokuUpper)
  736. {
  737. try
  738. {
  739. SetMpnProp(new MpnAttachProp(MPN.kousoku_upper, binaryReader.ReadString()), false);
  740. }
  741. catch { }
  742. }
  743. bool hasKousokuLower = binaryReader.ReadBoolean();
  744. if (hasKousokuLower)
  745. {
  746. try
  747. {
  748. SetMpnProp(new MpnAttachProp(MPN.kousoku_lower, binaryReader.ReadString()), false);
  749. }
  750. catch { }
  751. }
  752. // OnUpdateMeido();
  753. }
  754. private void DeserializeFace(BinaryReader binaryReader)
  755. {
  756. StopBlink();
  757. binaryReader.ReadString(); // read face header
  758. string header;
  759. while ((header = binaryReader.ReadString()) != "END_FACE")
  760. {
  761. SetFaceBlendValue(header, binaryReader.ReadSingle());
  762. }
  763. }
  764. }
  765. public class GravityEventArgs : EventArgs
  766. {
  767. public Vector3 LocalPosition { get; }
  768. public bool IsSkirt { get; }
  769. public GravityEventArgs(bool isSkirt, Vector3 localPosition)
  770. {
  771. LocalPosition = localPosition;
  772. IsSkirt = isSkirt;
  773. }
  774. }
  775. public struct PoseInfo
  776. {
  777. public string PoseGroup { get; }
  778. public string Pose { get; }
  779. public bool CustomPose { get; }
  780. public PoseInfo(string poseGroup, string pose, bool customPose = false)
  781. {
  782. PoseGroup = poseGroup;
  783. Pose = pose;
  784. CustomPose = customPose;
  785. }
  786. public void Serialize(BinaryWriter binaryWriter)
  787. {
  788. binaryWriter.Write(PoseGroup);
  789. binaryWriter.Write(Pose);
  790. binaryWriter.Write(CustomPose);
  791. }
  792. public static PoseInfo Deserialize(BinaryReader binaryReader)
  793. {
  794. return new PoseInfo
  795. (
  796. binaryReader.ReadString(),
  797. binaryReader.ReadString(),
  798. binaryReader.ReadBoolean()
  799. );
  800. }
  801. }
  802. }