Meido.cs 30 KB

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