using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace SelakAR { public class ResetPoseRootToZero : MVRScript { private const float SpacerHeight = 25f; private const string StatusReady = "Ready."; private const string StatusBusy = "Busy....................."; private const string SelectedPersonOptionFemale = "Female"; private const string SelectedPersonOptionMale = "Male"; private const string SelectedPersonOptionBoth = "Couple"; private JSONStorableBool isMaleYReversed = new JSONStorableBool("Male: Y Rotation Reversed", false); private JSONStorableStringChooser selectedPerson; private bool isBusy = false; JSONStorableString stringStatus; private Atom femaleAtom = null; private Atom maleAtom = null; public override void Init() { UIDynamicButton button; string instructionsText = "Reset pose root to zero\n" + "Make your poses universal and have them load perfectly in-place, anywhere. This is a scene plugin and it is to be used in the respective scene.\n\n" + "Steps (single person):\n\n" + "1: Choose \"Female\" or \"Male\" in the dropdown menu.\n\n" + "2: Go to \"Pose Presets\" and load a pose.\n\n" + "3: If the root control is tilted, click the green button already.\n\n" + "4: Manually move the atom so that the red sphere is where you want the center of the pose to be.\n\n" + "5: Click the green button.\n\n" + "6: Save your pose, with \"Store Root Node In Pose\" off.\n\n" + "Steps (couple version):\n\n" + "1: Choose \"Couple\" in the dropdown menu.\n\n" + "2: Decide if you want the male's Y rotation reversed.\n\n" + "3: Go to \"Pose Presets\" and load a pose. Do that for both the male and the female.\n\n" + "4: Click the green button.\n\n" + "5: Manually move one of the atoms so the red sphere is where you want the center of the couple's pose to be.\n\n" + "6: Now the other person is misaligned. Match the position of both persons with one of the \"match\" buttons.\n\n" + "7: Click the green button again.\n\n" + "8: If necessary, rotate che couple (for consistency in your poses) with the \"Rotate ...\" buttons. Use the signs on the floor for reference. Then you can click the green button again.\n\n" + "9: Save both poses, with \"Store Root Node In Pose\" off.\n\n" + "The red sphere\n\n" + "The red sphere is the center of the scene (0, 0, 0). So, if you want the root of the pose to be the butt, move the person so that the red sphere is just below the butt, slightly touching it.\n\n" + "Physics\n\n" + "Physics is taken into account, and enabled and disabled as required. Give some milliseconds to the buttons to manage that.\n\n" + "Options\n\n" + "Disable Plugins and Auto Behaviors: Sets both atoms to their most neutral state to prevent interference with the pose. Only needed if you've loaded plugins or if you're using this plugin in your own scene.\n\n" + "Collision On/Off: These always apply to both persons. Some poses load more correctly with collisions enabled. However, if you need to fine-tune the pose and move the atoms around, you may want to disable it to prevent physics explosions.\n\n" + "Male Y Rotation Reversed: Not recommended. This option only works when the \"Couple\" option is selected. The blue arrow is \"forward\", so, if you want, you can have the male's Y rotation reversed, so he looks towards the female. This, however, limits the sex positions to \"vertical\" ones only: when you're making use of your poses, you may need to rotate the male atom. Thus, it is better to leave this option unchecked.\n\n" + "M match F, and F match M: These buttons move a person to the exact position and angle of the other one. Before using this, reset the root controls to zero (green button). If the male's Y rotation is reversed, that is taken into account.\n\n" + "Rotate Clockwise / Counterclockwise: If the loaded pose(s) look(s) towards an angle that you don't like, you can use these buttons to rotate the atom(s) and then reset the pose again with the green button. Use the signs on the floor for reference. If the \"Couple\" option is selected, both atoms are rotating. These buttons work at steps of 90°. If the original pose is weirdly angled, e.g. at 45°, then you need to rotate one person manually, and then use the \"match\" buttons for the other one.\n\n" + "Move atoms to position zero: If you want to load poses that have been made with this plugin, you first want to position both atoms at the center of the scene."; CreateTextField(new JSONStorableString("text", instructionsText), false).height = 1200; button = CreateButton("Disable Plugins and Auto Behaviors", true); button.button.onClick.AddListener(DisablePluginsAndAutoBehaviors); button = CreateButton("Collision On", true); button.button.onClick.AddListener(() => SetCollision(true)); button = CreateButton("Collision Off", true); button.button.onClick.AddListener(() => SetCollision(false)); CreateSpacer(true).height = SpacerHeight; List choices = new List(); choices.Add(SelectedPersonOptionFemale); choices.Add(SelectedPersonOptionMale); choices.Add(SelectedPersonOptionBoth); selectedPerson = new JSONStorableStringChooser( "Selected Person", choices, SelectedPersonOptionFemale, "Work on:"); UIDynamicPopup udp = CreatePopup(selectedPerson, true); udp.labelWidth = 300f; CreateToggle(isMaleYReversed, true); CreateSpacer(true).height = SpacerHeight; button = CreateButton("Reset Root", true); button.buttonColor = Color.green; button.button.onClick.AddListener(() => { if (isBusy == false) StartCoroutine(ResetRoot()); }); button = CreateButton("Reset Root's Y Pos Only", true); button.buttonColor = Color.green; button.button.onClick.AddListener(() => { if (isBusy == false) StartCoroutine(ResetRootYPosOnly()); }); CreateSpacer(true).height = SpacerHeight; button = CreateButton("M match F", true); button.button.onClick.AddListener(() => { if (isBusy == false) StartCoroutine(MatchOtherPersonRoot(true)); }); button = CreateButton("F match M", true); button.button.onClick.AddListener(() => { if (isBusy == false) StartCoroutine(MatchOtherPersonRoot(false)); }); button = CreateButton("Rotate Clockwise", true); button.button.onClick.AddListener(() => { if (isBusy == false) StartCoroutine(RotateAtom(true)); }); button = CreateButton("Rotate Counterclockwise", true); button.button.onClick.AddListener(() => { if (isBusy == false) StartCoroutine(RotateAtom(false)); }); CreateSpacer(true).height = SpacerHeight; button = CreateButton("Move atoms to position zero", true); button.button.onClick.AddListener(() => { if (isBusy == false) StartCoroutine(MoveBothAtomsToZero()); }); CreateSpacer(true).height = SpacerHeight; stringStatus = new JSONStorableString("text", StatusReady); CreateTextField(stringStatus, true).height = 6; femaleAtom = LoadAtom("Female"); maleAtom = LoadAtom("Male"); } private Atom LoadAtom(string name) { Atom atom = SuperController.singleton.GetAtomByUid(name); try { if (atom == null) { SuperController.singleton.Error("Can't find person atom (\"" + name + "\"). " + "Please name your atoms \"Male\" and \"Female\" so the plugin can find them."); } } catch (Exception e) { SuperController.LogError("Exception caught: " + e); } return atom; } private void SetBusy(bool state) { isBusy = state; if (isBusy) stringStatus.val = StatusBusy; else stringStatus.val = StatusReady; } private List GetPersonList(bool both) { List atoms = new List(); if(both == true || selectedPerson.val == SelectedPersonOptionBoth) { atoms.Add(femaleAtom); atoms.Add(maleAtom); } else if (selectedPerson.val == SelectedPersonOptionFemale) { atoms.Add(femaleAtom); } else if (selectedPerson.val == SelectedPersonOptionMale) { atoms.Add(maleAtom); } return atoms; } private List GetBothPersonsList() { return GetPersonList(true); } private List GetSelectedPersonsList() { return GetPersonList(false); } private void DisablePluginsAndAutoBehaviors() { foreach (Atom atom in GetBothPersonsList()) { // Disable all plugins MVRPluginManager manager = atom.GetStorableByID("PluginManager") as MVRPluginManager; if (manager != null) { var plugins = manager.GetJSON(true, true)["plugins"].AsObject; foreach (var pluginId in plugins.Keys) { var jsonPlugins = atom.GetStorableIDs().Where((sid) => sid.StartsWith(pluginId)).Select((sid) => atom.GetStorableByID(sid)); foreach (var jsp in jsonPlugins) jsp.SetBoolParamValue("enabled", false); } } // Disable/adjust some auto behaviors JSONStorable js; js = atom.GetStorableByID("AutoExpressions"); js.GetBoolJSONParam("enabled").val = false; js = atom.GetStorableByID("EyelidControl"); js.GetBoolJSONParam("blinkEnabled").val = false; js = atom.GetStorableByID("Eyes"); js.GetStringChooserJSONParam ("lookMode").val = "Custom"; if(atom == femaleAtom) js.GetStringChooserJSONParam ("targetAtom").val = "Male"; else js.GetStringChooserJSONParam ("targetAtom").val = "Female"; js.GetStringChooserJSONParam ("targetControl").val = "headControl"; } } private void SetCollision(bool state) { femaleAtom.SetCollisionEnabled(state); maleAtom.SetCollisionEnabled(state); } private IEnumerator PreparationsForResettingRoot() { SetBusy(true); // Enable physics femaleAtom.SetFreezePhysics(false); maleAtom.SetFreezePhysics(false); // Wait for it to kick in. yield return new WaitForSeconds(0.2f); } private void FinalizationsForResettingRoot() { SetBusy(false); } private IEnumerator PreparationsForMovingAtom() { SetBusy(true); // Freeze physics femaleAtom.SetFreezePhysics(true); maleAtom.SetFreezePhysics(true); // Wait for it to kick in. yield return new WaitForSeconds(0.2f); } private IEnumerator FinalizationsForMovingAtom() { yield return new WaitForSeconds(0.2f); // Enable physics again femaleAtom.SetFreezePhysics(false); maleAtom.SetFreezePhysics(false); SetBusy(false); } private void ResetRootRotation(Atom atom) { JSONStorable atomControl = atom.GetStorableByID("control"); float yEulerAngle = 0f; if (selectedPerson.val == SelectedPersonOptionBoth && atom == maleAtom && isMaleYReversed.val == true) yEulerAngle = 180f; atomControl.transform.eulerAngles = new Vector3(0, yEulerAngle, 0); } private IEnumerator ResetRoot() { yield return PreparationsForResettingRoot(); foreach (Atom atom in GetSelectedPersonsList()) { JSONStorable atomControl = atom.GetStorableByID("control"); atomControl.GetBoolJSONParam("detachControl").val = true; atomControl.transform.position = new Vector3(0, 0, 0); ResetRootRotation(atom); atomControl.GetBoolJSONParam("detachControl").val = false; } FinalizationsForResettingRoot(); } private IEnumerator ResetRootYPosOnly() { yield return PreparationsForResettingRoot(); foreach (Atom atom in GetSelectedPersonsList()) { JSONStorable atomControl = atom.GetStorableByID("control"); atomControl.GetBoolJSONParam("detachControl").val = true; atomControl.transform.position = new Vector3( atomControl.transform.position.x, 0, atomControl.transform.position.z ); //ResetRootRotation(atom); atomControl.GetBoolJSONParam("detachControl").val = false; } FinalizationsForResettingRoot(); } private IEnumerator MatchOtherPersonRoot(bool moveMale) { yield return PreparationsForMovingAtom(); Atom movingAtom; Atom targetAtom; if (moveMale) { movingAtom = maleAtom; targetAtom = femaleAtom; } else { movingAtom = femaleAtom; targetAtom = maleAtom; } JSONStorable movingControl = movingAtom.GetStorableByID("control"); JSONStorable targetControl = targetAtom.GetStorableByID("control"); // Ensure root control is attached: movingControl.GetBoolJSONParam("detachControl").val = false; // Move movingControl.transform.position = targetControl.transform.position; // Rotate if (isMaleYReversed.val == false) { movingControl.transform.eulerAngles = targetControl.transform.eulerAngles; } else { Vector3 mirrored = targetControl.transform.eulerAngles; mirrored.x = -mirrored.x; mirrored.z = -mirrored.z; movingControl.transform.eulerAngles = mirrored; movingControl.transform.eulerAngles += new Vector3(0, 180, 0); } yield return FinalizationsForMovingAtom(); } private IEnumerator RotateAtom(bool clockwise) { yield return PreparationsForMovingAtom(); foreach (Atom atom in GetSelectedPersonsList()) { JSONStorable atomControl = atom.GetStorableByID("control"); // Ensure root control is attached: atomControl.GetBoolJSONParam("detachControl").val = false; // Rotate if (clockwise) atomControl.transform.Rotate(0, 90, 0); else atomControl.transform.Rotate(0, -90, 0); } yield return FinalizationsForMovingAtom(); } private IEnumerator MoveBothAtomsToZero() { yield return PreparationsForMovingAtom(); foreach (Atom atom in GetBothPersonsList()) { JSONStorable movingControl = atom.GetStorableByID("control"); // Ensure root control is attached: movingControl.GetBoolJSONParam("detachControl").val = false; // Position movingControl.transform.position = new Vector3(0, 0, 0); // Angle if (atom == maleAtom && isMaleYReversed.val == true) movingControl.transform.eulerAngles = new Vector3(0, 180, 0); else movingControl.transform.eulerAngles = new Vector3(0, 0, 0); } yield return FinalizationsForMovingAtom(); } } }