Пример #1
0
 public void OnDrop(object sender, DragEventArgs e)
 {
     if (!e.Data.GetDataPresent(DataFormats.FileDrop))
     {
         return;
     }
     string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
     if (files != null)
     {
         foreach (string file in files)
         {
             string extension = Path.GetExtension(file)?.ToLower();
             //判斷當前的檔案副檔名是否為stl,不是的話則跳過
             if (extension != null && !extension.Equals(".stl"))
             {
                 continue;
             }
             BoneModel model = new BoneModel
             {
                 FilePath         = file,
                 MarkerId         = "",
                 BoneDiffuseColor = System.Windows.Media.Color.FromArgb(255, 40, 181, 187),
                 Transform        = new MatrixTransform3D()
             };
             model.LoadModel();
             MultiAngleViewModel.NormalModelCollection.Add(model);
         }
     }
     MultiAngleViewModel.ResetCameraPosition();
 }
Пример #2
0
        void StartListening(BoneModel bone)
        {
            PuppetReprModel slave = bone.reprs[PuppetModel.key] as PuppetReprModel;

            // Listen to trigger and collision notifiers for each bone.pheasy (when possible)
            if (slave.pheasy)
            {
                if (slave.pheasy.collisionNotifier)
                {
                    slave.pheasy.collisionNotifier.invokeStayEvents = true;
                    slave.pheasy.collisionNotifier.onRbEnter.AddListener(col => OnRbTouchStart(col, bone));
                    slave.pheasy.collisionNotifier.onRbStay.AddListener(col => OnRbTouchStay(col, bone));
                    slave.pheasy.collisionNotifier.onRbExit.AddListener(col => OnRbTouchEnd(col, bone));
                }

                if (model.hoverDetectionSystem == HoverDetectionSystem.Triggers)
                {
                    for (int tn = 0; tn < slave.pheasy.triggerNotifiers.Length; tn++)
                    {
                        slave.pheasy.triggerNotifiers[tn].onRbEnter.AddListener(col => OnRbEnter(col, bone));
                        slave.pheasy.triggerNotifiers[tn].onRbExit.AddListener(col => OnRbExit(col, bone));
                    }
                }
            }
        }
Пример #3
0
        public void AddAvatar(AvatarController avatar)
        {
            this._avatars.Add(avatar);

            avatar.ControllerStart();

            if (applyThisLayerToAvatars != "")
            {
                int layer = LayerMask.NameToLayer(applyThisLayerToAvatars);
                if (layer >= 0)
                {
                    foreach (BodyModel body in avatar.model.bodies)
                    {
                        BoneModel rootBone = body.root.root;

                        foreach (KeyValuePair <string, ReprModel> entry in rootBone.point.reprs)
                        {
                            BasicHelpers.ApplyLayerRecursively(entry.Value.transformRef, layer);
                        }
                    }
                }
            }

            onAvatarEntered.Invoke(avatar.model.view);
        }
Пример #4
0
        /// <summary>
        /// DeleteBoneCommad 的實作內容,刪除Bone模型項目
        /// </summary>
        private void DeleteBoneItem(object o)
        {
            ListView boneListView = _mainWindow.BoneListView;

            if (boneListView.SelectedItem != null)
            {
                //選擇的BoneModel
                BoneModel selectedModelItem = (BoneModel)boneListView.SelectedItem;

                int temp = boneListView.SelectedIndex;

                var ballCollection = MainViewModel.ProjData.BoneCollection;

                ballCollection.Remove(selectedModelItem);

                //刪減之後數量若跟舊的索引值一樣,代表選項在最後一個
                if (ballCollection.Count == temp)
                {
                    boneListView.SelectedIndex = ballCollection.Count - 1;
                }
                else//不是的話則維持原索引值
                {
                    boneListView.SelectedIndex = temp;
                }
                ListViewItem item = boneListView.ItemContainerGenerator.ContainerFromIndex(boneListView.SelectedIndex) as ListViewItem;
                item?.Focus();
            }
        }
Пример #5
0
        void OnRbTouchStay(Collision collision, BoneModel bone)
        {
            stayedContact = model.contacts.Find(c => c.contactable.pheasy.rb == collision.rigidbody);

            if (stayedContact == null || stayedContact.contactable == null)
            {
                return;
            }

            stayedContactable = stayedContact.contactable;

            if (stayedContactable)
            {
                Contact contact = model.contacts.Find(c => c.contactable == stayedContactable);

                if (contact != null)
                {
                    BoneCollisionModel boneCollision = contact.bonesTouching.Find(bc => bc.bone == bone);

                    if (boneCollision != null)
                    {
                        collision.GetContacts(boneCollision.points);
                    }
                }
            }
        }
Пример #6
0
        void OnRbEnter(Rigidbody enteredRb, BoneModel bone)
        {
            ContactableView contactable = enteredRb.GetComponent <ContactableView>();

            if (contactable)
            {
                Contact contact = model.contacts.Find(c => c.contactable == contactable);

                if (contact != null)
                {
                    // Existing contact
                    if (!contact.bonesEntered.Contains(bone))
                    {
                        contact.bonesEntered.Add(bone);
                    }
                }
                else
                {
                    // New valid contact
                    contact      = AddContact(contactable);
                    contact.type = ContactType.None;
                    contact.bonesEntered.Add(bone);
                }
            }
        }
Пример #7
0
        public static BoneBody AddBone(this ConnectionSlotBody slotBody, BoneModel boneModel)
        {
            var slot = slotBody.Model;

            slot.IsOccupied = true;

            var slotPos   = slotBody.State.Position;
            var slotSize  = slotBody.Model.Size;
            var centerLoc = Vector2D.FromLengthAndAngle((boneModel.Length + slotSize) * 0.5, slotPos.Angular);

            var bonePos = new ALVector2D(slotPos.Angular, slotPos.Linear + centerLoc);

            var rectBody = CreateRectangle(boneModel.Thickness, boneModel.Length, 0.00001, bonePos);

            var newBone = rectBody.CopyAsBone(slotBody.ModelId);

            newBone.Model  = boneModel;
            newBone.Parent = slotBody;

            var joints = slotBody.ConnectWith(newBone, (2 * bonePos.Linear + 8 * slotBody.State.Position.Linear) * 0.1f);

            Will.Instance.AddBody(newBone);
            Will.Instance.AddJoint(joints.Item1);
            Will.Instance.AddJoint(joints.Item2);

            return(newBone);
        }
Пример #8
0
        void OnRbTouchStart(Collision collision, BoneModel bone)
        {
            ContactableView contactable = collision.rigidbody.GetComponent <ContactableView>();

            if (contactable)
            {
                Contact contact = model.contacts.Find(c => c.contactable == contactable);

                BoneCollisionModel boneCollision;

                if (contact != null)
                {
                    // Existing contact

                    boneCollision = contact.bonesTouching.Find(bc => bc.bone == bone);

                    if (boneCollision == null)
                    {
                        boneCollision = new BoneCollisionModel(bone);
                        collision.GetContacts(boneCollision.points);
                        contact.bonesTouching.Add(boneCollision);
                    }
                }
                else
                {
                    // New valid contact
                    contact      = AddContact(contactable);
                    contact.type = ContactType.None;

                    boneCollision = new BoneCollisionModel(bone);
                    collision.GetContacts(boneCollision.points);
                    contact.bonesTouching.Add(boneCollision);
                }
            }
        }
Пример #9
0
        public void DeleteItem(object sender, RoutedEventArgs e)
        {
            if (BoneListView.SelectedItem != null)
            {
                //選擇的BoneModel
                BoneModel selectedModelItem = (BoneModel)BoneListView.SelectedItem;

                int temp = BoneListView.SelectedIndex;

                var ballCollection = MainViewModel.ProjData.BoneCollection;

                ballCollection.Remove(selectedModelItem);

                //刪減之後數量若跟舊的索引值一樣,代表選項在最後一個
                if (ballCollection.Count == temp)
                {
                    BoneListView.SelectedIndex = ballCollection.Count - 1;
                }
                else//不是的話則維持原索引值
                {
                    BoneListView.SelectedIndex = temp;
                }

                ListViewItem item = BoneListView.ItemContainerGenerator.ContainerFromIndex(BoneListView.SelectedIndex) as ListViewItem;
                if (item != null)
                {
                    item.Focus();
                }
            }
        }
Пример #10
0
        void LateBoneStart(BoneModel bone)
        {
            if (!bone.reprs.ContainsKey(PuppetModel.key))
            {
                Debug.Log("Bone " + bone + " does not have a " + PuppetModel.key + " representation");
                return;
            }

            PuppetReprModel slave = bone.reprs[PuppetModel.key] as PuppetReprModel;

            if (!slave.usePhysics)
            {
                return;
            }

            if (bone.parent != null)
            {
                // Ignore part-parent collisions
                for (int b = 0; b < bone.part.parent.bones.Count; b++)
                {
                    BoneModel parentBone = bone.part.parent.bones[b];

                    if (parentBone.reprs.ContainsKey(PuppetModel.key))
                    {
                        PuppetReprModel parentSlave = parentBone.reprs[PuppetModel.key] as PuppetReprModel;
                        slave.pheasy.IgnoreCollisions(parentSlave.pheasy, true, true);
                    }
                }
            }

            slave.specificView.onPhysicsReady.Invoke();

            slave.ready = true;
        }
Пример #11
0
    public void ApplyToArmature()
    {
        if (!hand)
        {
            return;
        }

        if (!hand.proxyHand)
        {
            Debug.LogError("Hand.ProxyHand is NULL!");
            return;
        }

        if (hand.fingers.Length != hand.proxyHand.master.fingers.Length)
        {
            Debug.LogError("Hand.fingers and Hand.proxyHand.master.fingers have to have the same length!");
            return;
        }

        MasterBoneModel masterWristBone = hand.proxyHand.master.wrist as MasterBoneModel;

        if (masterWristBone.armatureBone)
        {
            if (space == Space.World)
            {
                masterWristBone.armatureBone.rotation = hand.wrist.transformRef.rotation * masterWristBone.relativeToOriginalArmatureWorld;
            }
            else
            {
                /* unsupported */

                Debug.LogError("Local space is not supported!");
                return;
            }
        }


        for (int f = 0; f < hand.fingers.Length; f++)
        {
            for (int b = 0; b < hand.fingers[f].bones.Length; b++)
            {
                BoneModel bone = hand.fingers[f].bones[b];

                MasterBoneModel masterBone = hand.proxyHand.master.fingers[f].bones[b] as MasterBoneModel;

                if (masterBone.armatureBone)
                {
                    if (space == Space.World)
                    {
                        masterBone.armatureBone.rotation = bone.transformRef.rotation * masterBone.relativeToOriginalArmatureWorld;
                    }
                    else
                    {
                        /* unreachable */
                    }
                }
            }
        }
    }
Пример #12
0
    public MasterBoneModel CastBone(BoneModel bone)
    {
        MasterBoneModel masterBone = bone.gameObject.AddComponent <MasterBoneModel>();

        masterBone.transformRef = bone.transformRef;
        Destroy(bone);
        return(masterBone);
    }
Пример #13
0
    public Bone GetSettedBone()
    {
        DiceParameter diceParameter = Mediator.Instance.GameConfig.DiceParameters.Find(dice => dice.DiceID == PlayerPrefs.GetString("SelectedDiceID", "WB"));
        BoneModel     boneModel     = diceParameter.BonePrefab.GetComponentInChildren <BoneModel>();

        boneModel.BodyMaterial.color   = diceParameter.BodyColor;
        boneModel.PointsMaterial.color = diceParameter.PointsColor;

        return(diceParameter.BonePrefab);
    }
Пример #14
0
        void BoneUpdate(BoneModel bone)
        {
            if (bone.point.master == null || !bone.point.reprs.ContainsKey(PuppetModel.key))
            {
                return;
            }

            ReprModel       master;
            PuppetReprModel slave = bone.point.reprs[PuppetModel.key] as PuppetReprModel;

            if (!slave.usePhysics)
            {
                return;
            }

            if (!bone.reprs.ContainsKey(model.mimicRepr))
            {
                Debug.LogWarning("Bone " + bone.name + " does not have a " + model.mimicRepr + " representation. Puppet goal cannot be updated for this bone");
                return;
            }

            if (slave.constraint.connectedBody)
            {
                master = bone.point.reprs[model.mimicRepr];

                if (master.localRotZ < slave.maxLocalRotZ && master.localRotZ > slave.minLocalRotZ)
                {
                    slave.goal.rotation = slave.goal.parent.rotation * master.localRotation;
                }
                else
                {
                    slave.goal.localRotation = slave.fixedLocalRot;
                }
            }
            else
            {
                master = bone.point.reprs[model.mimicRepr];

                slave.goal.rotation = master.transformRef.rotation;
                slave.goal.position = master.transformRef.position;

                if (model.forceUnconnected)
                {
                    slave.pheasy.rb.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
                }
                else
                {
                    slave.pheasy.rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
                }

                slave.pheasy.rb.isKinematic = model.forceUnconnected;
                slave.constraint.force      = model.forceUnconnected;
            }
        }
Пример #15
0
        public ProjectData(SerializationInfo info, StreamingContext context)
        {
            Name             = (string)info.GetValue("Name", typeof(string));
            ID               = (string)info.GetValue("ID", typeof(string));
            Institution      = (string)info.GetValue("Institution", typeof(string));
            RegFilePath      = (string)info.GetValue("RegFilePath", typeof(string));
            IsRegInitialized = (bool)info.GetValue("IsRegInitialized", typeof(bool));
            IsNavSet         = (bool)info.GetValue("IsNavSet", typeof(bool));
            Stage1Red        = (float)info.GetValue("Stage1Red", typeof(float));
            Stage1Green      = (float)info.GetValue("Stage1Green", typeof(float));
            Stage1Blue       = (float)info.GetValue("Stage1Blue", typeof(float));
            Stage2Red        = (float)info.GetValue("Stage2Red", typeof(float));
            Stage2Green      = (float)info.GetValue("Stage2Green", typeof(float));
            Stage2Blue       = (float)info.GetValue("Stage2Blue", typeof(float));
            DA               = (float)info.GetValue("DA", typeof(float));
            FDA              = (float)info.GetValue("FDA", typeof(float));
            HDA              = (float)info.GetValue("HDA", typeof(float));
            DD               = (float)info.GetValue("DD", typeof(float));
            PDD              = (float)info.GetValue("PDD", typeof(float));
            FirstNavigation  = (string)info.GetValue("FirstNavigation", typeof(string));
            IsNavDone        = (bool)info.GetValue("IsNavDone", typeof(bool));

            int count = (int)info.GetValue("BallCollection_Count", typeof(int));
            ObservableCollection <BallModel> ballCollection = new ObservableCollection <BallModel>();

            for (int i = 0; i < count; i++)
            {
                BallModel ballModel = (BallModel)info.GetValue("BallCollection_" + i, typeof(BallModel));
                ballModel.CreateBall();
                ballCollection.Add(ballModel);
            }
            BallCollection = ballCollection;

            count = (int)info.GetValue("BoneCollection_Count", typeof(int));
            ObservableCollection <BoneModel> boneCollection = new ObservableCollection <BoneModel>();

            for (int i = 0; i < count; i++)
            {
                BoneModel boneModel = (BoneModel)info.GetValue("BoneCollection_" + i, typeof(BoneModel));
                boneCollection.Add(boneModel);
            }
            BoneCollection = boneCollection;

            count = (int)info.GetValue("TargetCollection_Count", typeof(int));
            ObservableCollection <BoneModel> targetCollection = new ObservableCollection <BoneModel>();

            for (int i = 0; i < count; i++)
            {
                BoneModel targetModel = (BoneModel)info.GetValue("TargetCollection_" + i, typeof(BoneModel));
                targetCollection.Add(targetModel);
            }
            TargetCollection = targetCollection;
        }
Пример #16
0
        public static BoneBody AddBoneBody(BoneModel boneModel)
        {
            var slotBody = Will.Instance.Bodies.RandomOrDefault <ConnectionSlotBody>(b => !b.Model.IsOccupied);

            if (slotBody == null)
            {
                return(null); // TODO: replace with unconnected bone
            }

            var newBone = slotBody.AddBone(boneModel);

            return(newBone);
        }
Пример #17
0
        public override sealed void InitFingerGesture()
        {
            base.InitFingerGesture();

            if (_model.finger.tip.bone.parent)
            {
                strengthDirector = _model.finger.tip.bone.parent;
            }
            else
            {
                strengthDirector = _model.finger.tip.bone;
            }
        }
Пример #18
0
        // If bone1 -> Finger rotation. If bone 2 -> Finger strength
        public static float GetBoneRotLerp(BoneModel bone, float maxLocalRotZ, float minLocalRotZ)
        {
            float localRotZ = bone.transformRef.localRotation.eulerAngles.z;

            if (localRotZ <= maxLocalRotZ && localRotZ >= minLocalRotZ)
            {
                return(Mathf.InverseLerp(maxLocalRotZ, minLocalRotZ, localRotZ));
            }
            else if (localRotZ < 0.0f || localRotZ > maxLocalRotZ || (localRotZ >= 0.0f && localRotZ < 180.0f))
            {
                return(0.0f);
            }
            else
            {
                return(1.0f);
            }
        }
Пример #19
0
        public void UpdateData(ProjectData projectData)
        {
            this.Name             = projectData.Name;
            this.ID               = projectData.ID;
            this.Institution      = projectData.Institution;
            this.RegFilePath      = projectData.RegFilePath;
            this.IsRegInitialized = projectData.IsRegInitialized;
            this.IsNavSet         = projectData._isNavSet;
            this.Stage1Red        = projectData.Stage1Red;
            this.Stage1Green      = projectData.Stage1Green;
            this.Stage1Blue       = projectData.Stage1Blue;
            this.Stage2Red        = projectData.Stage2Red;
            this.Stage2Green      = projectData.Stage2Green;
            this.Stage2Blue       = projectData.Stage2Blue;

            this.DA  = projectData.DA;
            this.FDA = projectData.FDA;
            this.HDA = projectData.HDA;
            this.DD  = projectData.DD;
            this.PDD = projectData.PDD;


            this.FirstNavigation = projectData.FirstNavigation;
            this.IsNavDone       = projectData.IsNavDone;
            this.BallCollection.Clear();
            for (int i = 0; i < projectData.BallCollection.Count; i++)
            {
                BallModel ballModel = projectData.BallCollection[i];
                this.BallCollection.Add(ballModel);
            }

            this.BoneCollection.Clear();
            for (int i = 0; i < projectData.BoneCollection.Count; i++)
            {
                BoneModel boneModel = projectData.BoneCollection[i];
                this.BoneCollection.Add(boneModel);
            }

            this.TargetCollection.Clear();
            for (int i = 0; i < projectData.TargetCollection.Count; i++)
            {
                BoneModel targetModel = projectData.TargetCollection[i];
                this.TargetCollection.Add(targetModel);
            }
        }
Пример #20
0
        void OnRbTouchEnd(Collision collision, BoneModel bone)
        {
            ContactableView contactable = collision.rigidbody.GetComponent <ContactableView>();

            if (contactable)
            {
                Contact contact = model.contacts.Find(c => c.contactable == contactable);

                if (contact != null)
                {
                    BoneCollisionModel boneCollision = contact.bonesTouching.Find(bc => bc.bone == bone);

                    if (boneCollision != null)
                    {
                        contact.bonesTouching.Remove(boneCollision);
                    }
                }
            }
        }
Пример #21
0
        void OnRbExit(Rigidbody exitedRb, BoneModel bone)
        {
            ContactableView contactable = exitedRb.GetComponent <ContactableView>();

            if (contactable)
            {
                Contact contact = model.contacts.Find(c => c.contactable == contactable);

                if (contact != null)
                {
                    // Existing contact
                    if (contact.bonesEntered.Contains(bone))
                    {
                        bool stillEntered = false;

                        /*
                         * PuppetReprModel slave = bone.reprs.FirstOrDefault(r => r.Value is PuppetReprModel).Value as PuppetReprModel;
                         * PuppetReprModel slave = BasicHelpers.Get<ReprModel, PuppetReprModel>(bone.reprs);
                         */

                        PuppetReprModel slave = bone.reprs[PuppetModel.key] as PuppetReprModel;

                        // Remove bone only if exitedRb is not in any bone.pheasy.triggerNotifiers
                        for (int tn = 0; tn < slave.pheasy.triggerNotifiers.Length; tn++)
                        {
                            if (slave.pheasy.triggerNotifiers[tn].enteredRbs.Contains(exitedRb))
                            {
                                stillEntered = true;
                                break;
                            }
                        }

                        if (!stillEntered)
                        {
                            contact.bonesEntered.Remove(bone);
                        }
                    }
                }
            }
        }
Пример #22
0
        public static void GetBonesFromRootToTip(FingerModel finger, List <BoneModel> _orderedBones)
        {
            _orderedBones.Clear();

            BoneModel bone = finger.tip.bone;

            for (int i = 0; i < 50; i++)
            {
                _orderedBones.Add(bone);

                if (!bone.parent || bone == finger.root)
                {
                    break;
                }
                else
                {
                    bone = bone.parent;
                }
            }

            _orderedBones.Reverse();
        }
Пример #23
0
        void OverlapSphere(BoneModel bone, float radius, List <Rigidbody> foundRbs)
        {
            foundRbs.Clear();

            if (!bone.reprs.ContainsKey(PuppetModel.key))
            {
                return;
            }

            PuppetReprModel slave = bone.reprs[PuppetModel.key] as PuppetReprModel;

            Collider[] colliders = UnityEngine.Physics.OverlapSphere(slave.transformRef.position, radius);

            for (int c = 0; c < colliders.Length; c++)
            {
                if (!colliders[c].attachedRigidbody)
                {
                    continue;
                }

                foundRbs.Add(colliders[c].attachedRigidbody);
            }
        }
Пример #24
0
 public BoneViewModel(BoneModel model)
 {
     this.model = model;
 }
Пример #25
0
        /// <summary>
        ///將設置清單的每個Item Load進檔案,並轉成模型清單
        /// </summary>
        public void LoadSettingModel(object o)
        {
            //確保所有模型資訊都有set進去ModelInfo的資料
            for (int i = 0; i < ModelSettingCollection.Count; i++)
            {
                //Load模型檔,內部有防呆防止重複Load
                ModelSettingCollection[i].Load();

                OspModel  ospModel  = ModelSettingCollection[i].Osp;
                BoneModel boneModel = ModelSettingCollection[i].Bone;

                //確認有Load過且有沒有被加進去modelDataCollection
                if (ospModel.IsLoaded && !ospModel.IsAdded)
                {
                    MultiAngleViewModel.OspModelCollection.Add(ModelSettingCollection[i].Osp);
                    ModelSettingCollection[i].Osp.IsAdded = true;
                }
                //確認有Load過且有沒有被加進去modelDataCollection
                if (boneModel.IsLoaded && !boneModel.IsAdded)
                {
                    MainViewModel.ProjData.BoneCollection.Add(ModelSettingCollection[i].Bone);
                    ModelSettingCollection[i].Bone.IsAdded = true;
                    //除了頭部以外需要guide
                    if (!boneModel.MarkerId.Equals("Head") && !boneModel.MarkerId.Equals("C"))
                    {
                        ModelSettingCollection[i].Guide = new DraggableTriangle(boneModel.ModelCenter);

                        MultiAngleViewModel.TriangleModelCollection.Add(ModelSettingCollection[i].Guide);
                    }
                }
                //做bone 跟 osp transform的binding
                if (boneModel.IsLoaded && ospModel.IsAdded)
                {
                    var binding = new Binding("Transform");
                    binding.Source = boneModel;
                    binding.Mode   = BindingMode.OneWay;
                    BindingOperations.SetBinding(ospModel, Model3D.TransformProperty, binding);
                }
                //做bone 的轉移矩陣綁到 DraggableTriangle的ModelTransform上面
                if (boneModel.IsLoaded && ModelSettingCollection[i].Guide != null)
                {
                    var binding = new Binding("Transform");
                    binding.Source = boneModel;
                    binding.Mode   = BindingMode.OneWay;
                    BindingOperations.SetBinding(ModelSettingCollection[i].Guide, HelixToolkit.Wpf.SharpDX.GroupModel3D.TransformProperty, binding);
                }
            }
            //刪除modelDataCollection中已經從ModelInfoCollection移除的模型,
            for (int i = 0; i < MainViewModel.ProjData.BoneCollection.Count; i++)
            {
                BoneModel boneModel = MainViewModel.ProjData.BoneCollection[i] as BoneModel;
                //模型如果透過 - 移除 或是 因為換錯誤檔名造成IsLoaded 為false則直接移除
                if (boneModel.IsRemoved || !boneModel.IsLoaded)
                {
                    MainViewModel.ProjData.BoneCollection.RemoveAt(i);
                    boneModel.IsAdded = false;
                    i--;
                }
            }


            //刪除modelDataCollection中已經從ModelInfoCollection移除的模型,
            for (int i = 0; i < MultiAngleViewModel.OspModelCollection.Count; i++)
            {
                OspModel ospModel = MultiAngleViewModel.OspModelCollection[i] as OspModel;
                //模型如果透過 - 移除 或是 因為換錯誤檔名造成IsLoaded 為false則直接移除
                if (ospModel.IsRemoved || !ospModel.IsLoaded)
                {
                    MultiAngleViewModel.OspModelCollection.RemoveAt(i);
                    ospModel.IsAdded = false;
                    i--;
                }
            }

            Console.WriteLine("OSP 數量:" + MultiAngleViewModel.OspModelCollection.Count);
            Console.WriteLine("Bone  數量:" + MainViewModel.ProjData.BoneCollection.Count);



            MultiAngleViewModel.ResetCameraPosition();

            _modelSettingView.Hide();
        }
Пример #26
0
        public void LoadSettingModel(object o)
        {
            MainViewModel.ProjData.TargetCollection.Clear();
            MainViewModel.ProjData.BoneCollection.Clear();
            MultiAngleViewModel.OspModelCollection.Clear();
            MultiAngleViewModel.TriangleModelCollection.Clear();



            //讀取原始上下顎 加上 規劃後的轉移矩陣
            Matrix    plannedMatrix = ReadMatrixFile(_plannedMaxillaMatrix);
            BoneModel targetMaxilla = new BoneModel
            {
                FilePath         = MaxillaModel,
                IsRendering      = false,
                MarkerId         = "",
                ModelType        = ModelType.TargetMaxilla,
                BoneDiffuseColor = Color.FromArgb(255, 100, 100, 100),
                Transform        = new MatrixTransform3D(plannedMatrix.ToMatrix3D())
            };

            targetMaxilla.LoadModel();

            Matrix    plannedMandible = ReadMatrixFile(_plannedMandibleMatrix);
            BoneModel targetMandible  = new BoneModel
            {
                FilePath         = MandibleModel,
                IsRendering      = false,
                MarkerId         = "",
                ModelType        = ModelType.TargetMandible,
                BoneDiffuseColor = Color.FromArgb(255, 100, 100, 100),
                Transform        = new MatrixTransform3D(plannedMandible.ToMatrix3D())
            };

            targetMandible.LoadModel();

            //MainViewModel.Data.TargetCollection.Add(head);
            MainViewModel.ProjData.TargetCollection.Add(targetMaxilla);
            MainViewModel.ProjData.TargetCollection.Add(targetMandible);

            BoneModel head = new BoneModel
            {
                FilePath         = HeadModel,
                MarkerId         = "Head",
                ModelType        = ModelType.Head,
                BoneDiffuseColor = HeadDiffuseColor
            };

            head.LoadModel();
            BoneModel oriMaxilla = new BoneModel
            {
                FilePath         = MaxillaModel,
                ModelType        = ModelType.MovedMaxilla,
                MarkerId         = "Splint",
                BoneDiffuseColor = MaxillaDiffuseColor
            };

            oriMaxilla.LoadModel();
            BoneModel oriMandible = new BoneModel
            {
                FilePath         = MandibleModel,
                ModelType        = ModelType.MovedMandible,
                MarkerId         = "Splint",
                BoneDiffuseColor = MandibleDiffuseColor
            };

            oriMandible.LoadModel();

            MainViewModel.ProjData.BoneCollection.Add(head);
            MainViewModel.ProjData.BoneCollection.Add(oriMaxilla);
            MainViewModel.ProjData.BoneCollection.Add(oriMandible);

            //載入OSP模型
            OspModel headOsp = new OspModel
            {
                MarkerId     = "Head",
                FilePath     = HeadOsp,
                DiffuseColor = Color.FromArgb(50, 11, 243, 243)
            };

            headOsp.LoadOsp();

            OspModel mandibleOsp = new OspModel
            {
                MarkerId     = "C",
                FilePath     = MandibleOsp,
                DiffuseColor = Color.FromArgb(50, 2, 231, 2)
            };

            mandibleOsp.LoadOsp();

            //綁定下顎對稱面到下顎模型
            SetBinding(oriMandible, mandibleOsp, "Transform", HelixToolkit.Wpf.SharpDX.Model3D.TransformProperty, BindingMode.OneWay);
            MultiAngleViewModel.OspModelCollection.Add(headOsp);
            MultiAngleViewModel.OspModelCollection.Add(mandibleOsp);

            //標記屬於上顎的ID,綁定到目標上顎
            DraggableTriangle maxillaTargetTriangle = new DraggableTriangle(targetMaxilla.ModelCenter)
            {
                MarkerId    = "Maxilla",
                IsRendering = false,
                Transform   = targetMaxilla.Transform,
                ModelType   = ModelType.TargetMaxillaTriangle,
            };

            //標記屬於下顎的ID,綁定到目標下顎
            DraggableTriangle mandibleTargetTriangle = new DraggableTriangle(targetMandible.ModelCenter)
            {
                MarkerId    = "Mandible",
                IsRendering = false,
                Transform   = targetMandible.Transform,
                ModelType   = ModelType.TargetMandibleTriangle,
            };



            //將導航三角形綁定到導航的上顎
            DraggableTriangle maxillaTriangle = new DraggableTriangle(oriMaxilla.ModelCenter)
            {
                MarkerId     = "Maxilla",
                Transparency = 0.5f,
                IsRendering  = false,
                ModelType    = ModelType.MovedMaxillaTriangle,
            };

            SetBinding(oriMaxilla, maxillaTriangle, "Transform", HelixToolkit.Wpf.SharpDX.GroupModel3D.TransformProperty, BindingMode.OneWay);



            //將導航三角形綁定到導航的下顎
            DraggableTriangle mandibleTriangle = new DraggableTriangle(oriMandible.ModelCenter)
            {
                MarkerId     = "Mandible",
                Transparency = 0.5f,
                IsRendering  = false,
                ModelType    = ModelType.MovedMandibleTriangle,
            };

            SetBinding(oriMandible, mandibleTriangle, "Transform", HelixToolkit.Wpf.SharpDX.GroupModel3D.TransformProperty, BindingMode.OneWay);

            MultiAngleViewModel.TriangleModelCollection.Add(maxillaTargetTriangle);
            MultiAngleViewModel.TriangleModelCollection.Add(mandibleTargetTriangle);
            MultiAngleViewModel.TriangleModelCollection.Add(maxillaTriangle);
            MultiAngleViewModel.TriangleModelCollection.Add(mandibleTriangle);



            MultiAngleViewModel.ResetCameraPosition();

            MainViewModel.ProjData.IsNavSet = true;

            _navigateView.Hide();
        }
Пример #27
0
 public BoneCollisionModel(BoneModel bone)
 {
     this.bone   = bone;
     this.points = new List <ContactPoint>();
 }
Пример #28
0
        public override void ControllerUpdate()
        {
            base.ControllerUpdate();

            if (!model.inputDataProvider)
            {
                return;
            }

            if (!gameObject.activeSelf)
            {
                return;
            }

            masterReprKey = AvatarModel.key;

            model.inputDataProvider.UpdateData();

            // On hand tracked
            if (conf.ignoreTrackingLoss || model.inputDataProvider.confidence > conf.handTrackingLostUnderConfidence)
            {
                if (!model.handIsTracked)
                {
                    model.handIsTracked = true;
                    model.view.onHandTrackingRecovered.Invoke();
                }

                if (model.isPredicting)
                {
                    model.isPredicting = false;
                    model.view.onPredictionInterrupted.Invoke();
                }

                // If recovered from tracking loss
                if (timeSinceLastValidRecord != 0.0f)
                {
                    timeSinceLastValidRecord = 0.0f;

                    if (conf.hideMasterWhenLost)
                    {
                        model.hand.specificView.SetHandVisuals(true, masterReprKey);
                    }

                    if (conf.hideSlaveWhenLost)
                    {
                        model.hand.specificView.SetHandVisuals(true, PuppetModel.key);
                        model.hand.specificView.SetHandPhysics(true);
                    }
                }

                // Calculate clamping
                if (conf.useHandClamping)
                {
                    if (conf.gradualHandClamping)
                    {
                        handClampLerp = Mathf.Lerp(0.0f, conf.startDecreasingHandClampUnderConfidence, model.inputDataProvider.confidence);
                    }
                    else if (model.inputDataProvider.confidence > conf.startDecreasingHandClampUnderConfidence)
                    {
                        handClampLerp = 1.0f; // High confidece. Clamp set to highest
                    }
                    else
                    {
                        handClampLerp = 0.0f; // Low confidence. Clamp set to lowest
                    }
                    maxHandLinearSpeed  = Mathf.Lerp(conf.lowestHandLinearSpeed, model.highestLinearSpeed, handClampLerp);
                    maxHandAngularSpeed = Mathf.Lerp(conf.lowestHandAngularSpeed, model.highestAngularSpeed, handClampLerp);
                }
                else
                {
                    maxHandLinearSpeed  = -1.0f;
                    maxHandAngularSpeed = -1.0f;
                }

                // Update record
                if (model.configuration.recordTracking)
                {
                    InputHelpers.RecordBone(model.boneRecords, model.inputDataProvider.bones[0], 0);
                    // RecordBone(model.inputDataProvider.bones[1], 1);
                }

                // Noise reduction
                if (model.configuration.movingAverage != MovingAverage.None)
                {
                    model.inputDataProvider.bones[0] = ReduceNoise(model.boneRecords[0], 0);
                    // model.inputDataProvider.bones[1] = ReduceNoise(model.boneRecords[1], 1);
                }

                // Use or get what to move as wrist
                if (!model.moveThisAsWrist)
                {
                    model.moveThisAsWrist = model.rigMap.wrist.master.transformRef;
                }

                // Find a point of reference (if neede)
                if (model.replicatedTsf && !model.referenceTsf)
                {
                    model.referenceTsf = HPTK.core.trackingSpace;
                }

                // Update pos and rot for wrist and forearm
                if (conf.updateWrist && model.bonesToUpdate[0] != null)
                {
                    // Update wrist position and rotation
                    UpdateBoneTsfPos(model.rigMap.wrist.master, model.moveThisAsWrist, model.inputDataProvider.bones[0], maxHandLinearSpeed, model.referenceTsf, model.replicatedTsf);
                    UpdateBoneTsfRot(model.rigMap.wrist.master, model.moveThisAsWrist, model.inputDataProvider.bones[0], maxHandAngularSpeed, model.referenceTsf, model.replicatedTsf);
                }

                if (conf.updateForearm && model.bonesToUpdate[1] != null)
                {
                    // Optional
                    if (model.configuration.recordTracking)
                    {
                        InputHelpers.RecordBone(model.boneRecords, model.inputDataProvider.bones[1], 1);
                    }

                    // Update wrist position and rotation
                    UpdateBoneTsfPos(model.rigMap.forearm.master, model.rigMap.forearm.master.transformRef, model.inputDataProvider.bones[1], maxHandLinearSpeed, model.referenceTsf, model.replicatedTsf);
                    UpdateBoneTsfRot(model.rigMap.forearm.master, model.rigMap.forearm.master.transformRef, model.inputDataProvider.bones[1], maxHandAngularSpeed, model.referenceTsf, model.replicatedTsf); // Optional
                }
            }
            // On hand tracking loss
            else
            {
                if (model.handIsTracked)
                {
                    model.handIsTracked = false;
                    model.view.onHandTrackingLost.Invoke();
                }

                if (timeSinceLastValidRecord == 0.0f)
                {
                    if (conf.usePredictiveTrackingWhenLost)
                    {
                        acceleration       = (0.0f - wristSpeed) / conf.maxPredictionTime;
                        predictedDirection = wristVelocityDirection;
                        lastWristPosition  = model.rigMap.wrist.transformRef.position;

                        if (!model.isPredicting)
                        {
                            model.isPredicting = true;
                            model.view.onPredictionStart.Invoke();
                        }
                    }

                    if (conf.hideMasterWhenLost)
                    {
                        model.hand.specificView.SetHandVisuals(false, masterReprKey);
                    }

                    if (conf.hideSlaveWhenLost)
                    {
                        model.hand.specificView.SetHandVisuals(false, PuppetModel.key);
                        model.hand.specificView.SetHandPhysics(false);
                    }
                }

                timeSinceLastValidRecord = Time.timeSinceLevelLoad - timeOfLastRecord;

                if (conf.usePredictiveTrackingWhenLost)
                {
                    if (timeSinceLastValidRecord < conf.maxPredictionTime)
                    {
                        // Predict new position

                        // currentTime - lastRecordTime = 0 -> velocity = initial
                        // currentTime - lastRecordTime = maxPeedictionTime -> velocity = 0

                        newDisplacement    = wristSpeed * timeSinceLastValidRecord + 0.5f * acceleration * timeSinceLastValidRecord * timeSinceLastValidRecord;
                        predictedDirection = Quaternion.Slerp(wristDirectionChange, Quaternion.identity, timeSinceLastValidRecord / conf.maxPredictionTime) * wristVelocityDirection;

                        model.rigMap.wrist.transformRef.position = lastWristPosition + predictedDirection * newDisplacement;
                    }
                    else
                    {
                        if (model.isPredicting)
                        {
                            model.isPredicting = false;
                            model.view.onPredictionTimeLimitReached.Invoke();
                        }
                    }
                }
            }

            // On fingers tracked
            if (conf.ignoreTrackingLoss || model.inputDataProvider.confidence > conf.fingersTrackingLostUnderConfidence)
            {
                if (!model.fingersAreTracked)
                {
                    model.fingersAreTracked = true;
                    model.view.onFingersTrackingRecovered.Invoke();
                }

                // Calculate clamping
                if (conf.useFingerClamping)
                {
                    if (conf.gradualFingerClamping)
                    {
                        fingerClampLerp = Mathf.Lerp(0.0f, conf.startDecreasingFingerClampUnderConfidence, model.inputDataProvider.confidence);
                    }
                    else if (model.inputDataProvider.confidence > conf.startDecreasingFingerClampUnderConfidence)
                    {
                        fingerClampLerp = 1.0f; // High confidece. Clamp set to highest
                    }
                    else
                    {
                        fingerClampLerp = 0.0f; // Low confidence. Clamp set to lowest
                    }
                    // maxFingerLinearSpeed = Mathf.Lerp(model.lowestMasterBoneLinearSpeed, model.highestLinearSpeed, fingerClampLerp);
                    maxFingerAngularSpeed = Mathf.Lerp(conf.lowestFingerAngularSpeed, model.highestAngularSpeed, fingerClampLerp);
                }
                else
                {
                    // maxFingerLinearSpeed = -1.0f;
                    maxFingerAngularSpeed = -1.0f;
                }

                // Finger bones start at i=2 (i=0 -> wrist, i=1 -> forearm)
                for (int i = 2; i < model.bonesToUpdate.Length; i++)
                {
                    // Update record
                    if (model.configuration.recordTracking)
                    {
                        InputHelpers.RecordBone(model.boneRecords, model.inputDataProvider.bones[i], i);
                    }

                    // Noise reduction
                    if (model.configuration.movingAverage != MovingAverage.None)
                    {
                        model.inputDataProvider.bones[i] = ReduceNoise(model.boneRecords[i], i);
                    }

                    // Update only fingers rotation (assuming hierachy)
                    if (model.bonesToUpdate[i] != null)
                    {
                        BoneModel bone = model.bonesToUpdate[i];

                        AbstractTsf inputData = model.inputDataProvider.bones[i];

                        // Estimate rotation. Assuming sorted array of bones (i-1 => thumb0, i => thumb1, i+1 => thumb2)
                        if (!model.rigMap.thumb0 && bone == model.rigMap.thumb1)
                        {
                            inputData.rotation = EstimateLocalRotation(model.inputDataProvider.bones[i - 1], model.inputDataProvider.bones[i]);
                        }
                        else if (!model.rigMap.pinky0 && bone == model.rigMap.pinky1)
                        {
                            inputData.rotation = EstimateLocalRotation(model.inputDataProvider.bones[i - 1], model.inputDataProvider.bones[i]);
                        }

                        UpdateBoneTsfRot(bone.master, bone.master.transformRef, inputData, maxFingerAngularSpeed, null, null);
                    }
                }
            }
            // On fingers tracking loss
            else
            {
                if (model.fingersAreTracked)
                {
                    model.fingersAreTracked = false;
                    model.view.onFingersTrackingLost.Invoke();
                }
            }

            // If we need to record and confidence is good enough to record
            if (conf.usePredictiveTrackingWhenLost && model.inputDataProvider.confidence > conf.saveHandHistoricOverConfidence)
            {
                // Before updating wristVelocityDirection and wristPosition
                wristDirectionChange = Quaternion.FromToRotation(wristVelocityDirection, (model.rigMap.wrist.transformRef.position - wristPosition).normalized);

                // Before updating wristPosition and timeOfLastRecord
                deltaTime              = Time.timeSinceLevelLoad - timeOfLastRecord;
                displacement           = Vector3.Distance(wristPosition, model.rigMap.wrist.transformRef.position);
                displacement           = Mathf.Clamp(displacement, 0.0f, conf.maxPredictionDisplacement * deltaTime);
                wristSpeed             = displacement / deltaTime;
                wristVelocityDirection = (model.rigMap.wrist.transformRef.position - wristPosition).normalized;

                // Update wristPosition
                wristPosition = model.rigMap.wrist.transformRef.position;

                // Update timeOfLastRecord
                timeOfLastRecord = Time.timeSinceLevelLoad;
            }

            // Hand scaling
            if (model.updateRealScale && model.inputDataProvider.scale != model.hand.realScale)
            {
                model.hand.realScale = model.inputDataProvider.scale;
            }
        }
Пример #29
0
        private void CalcBtn_Click(object sender, RoutedEventArgs e)
        {
            BoneModel model1 = MainViewModel.ProjData.BoneCollection[1];
            BoneModel model2 = MainViewModel.ProjData.BoneCollection[2];

            HelixToolkit.Wpf.SharpDX.Core.Vector3Collection model1Position = model1.Geometry.Positions;
            HelixToolkit.Wpf.SharpDX.Core.Vector3Collection model2Position = model2.Geometry.Positions;

            double total  = 0;
            double Xtotal = 0;
            double Ytotal = 0;
            double Ztotal = 0;

            double totalfordev  = 0;
            double Xtotalfordev = 0;
            double Ytotalfordev = 0;
            double Ztotalfordev = 0;
            double RMStotal     = 0;

            double max  = 0;
            double XMax = 0;
            double YMax = 0;
            double ZMax = 0;

            if (model1Position.Count != model2Position.Count)
            {
                System.Windows.MessageBox.Show("模型不同  有錯");
                return;
            }
            for (int i = 0; i < model1Position.Count; i++)
            {
                double distance = Math.Sqrt(Math.Pow(model1Position[i].X - model2Position[i].X, 2)
                                            + Math.Pow(model1Position[i].Y - model2Position[i].Y, 2)
                                            + Math.Pow(model1Position[i].Z - model2Position[i].Z, 2));

                double Xdistance = Math.Abs(model1Position[i].X - model2Position[i].X);
                double Ydistance = Math.Abs(model1Position[i].Y - model2Position[i].Y);
                double Zdistance = Math.Abs(model1Position[i].Z - model2Position[i].Z);


                //目前距離大於最大的
                if (distance > max)
                {
                    max = distance;
                }

                if (Xdistance > XMax)
                {
                    XMax = Xdistance;
                }

                if (Ydistance > YMax)
                {
                    YMax = Ydistance;
                }

                if (Zdistance > ZMax)
                {
                    ZMax = Zdistance;
                }


                total    += distance;
                Xtotal   += Xdistance;
                Ytotal   += Ydistance;
                Ztotal   += Zdistance;
                RMStotal += distance * distance;
            }

            double avg  = Math.Round(total / model1Position.Count, 3);
            double xavg = Math.Round(Xtotal / model1Position.Count, 3);
            double yavg = Math.Round(Ytotal / model1Position.Count, 3);
            double zavg = Math.Round(Ztotal / model1Position.Count, 3);



            for (int i = 0; i < model1Position.Count; i++)
            {
                double distance = Math.Sqrt(Math.Pow(model1Position[i].X - model2Position[i].X, 2)
                                            + Math.Pow(model1Position[i].Y - model2Position[i].Y, 2)
                                            + Math.Pow(model1Position[i].Z - model2Position[i].Z, 2));

                double Xdistance = Math.Abs(model1Position[i].X - model2Position[i].X);
                double Ydistance = Math.Abs(model1Position[i].Y - model2Position[i].Y);
                double Zdistance = Math.Abs(model1Position[i].Z - model2Position[i].Z);



                totalfordev  += (distance - avg) * (distance - avg);
                Xtotalfordev += (Xdistance - xavg) * (Xdistance - xavg);
                Ytotalfordev += (Ydistance - yavg) * (Ydistance - yavg);
                Ztotalfordev += (Zdistance - zavg) * (Zdistance - zavg);
            }

            double Dev  = Math.Sqrt(totalfordev / model1Position.Count);
            double XDev = Math.Sqrt(Xtotalfordev / model1Position.Count);
            double YDev = Math.Sqrt(Ytotalfordev / model1Position.Count);
            double ZDev = Math.Sqrt(Ztotalfordev / model1Position.Count);

            double RMS = Math.Sqrt(RMStotal / model1Position.Count);



            Console.WriteLine("模型一:" + model1.FilePath);
            Console.WriteLine("模型二:" + model2.FilePath);
            Console.WriteLine("整體:" + avg + "±" + Math.Round(Dev, 3) + "  " + Math.Round(max, 3));



            Console.WriteLine("RMS Error:" + Math.Round(RMS, 3));

            Console.WriteLine("XYZ Mean:" + xavg + "±" + Math.Round(XDev, 3) + "  " + yavg + "±" + Math.Round(YDev, 3) + "  " + zavg + "±" + Math.Round(ZDev, 3));

            Console.WriteLine("XYZ Max:" + Math.Round(XMax, 3) + "  " + Math.Round(YMax, 3) + "  " + Math.Round(ZMax, 3) + "  ");


            Console.WriteLine("\n");
        }
Пример #30
0
        void BoneStart(BoneModel bone, BoneModel mainRoot)
        {
            if (!bone.reprs.ContainsKey(PuppetModel.key))
            {
                Debug.LogWarning("Bone " + bone.name + " does not have any " + PuppetModel.key + " representation. PuppetController cannot start this bone");
                return;
            }

            PuppetReprModel slave = bone.reprs[PuppetModel.key] as PuppetReprModel;

            // Set puppet model for each puppet representation
            slave.puppet = model;

            // Set thumb bones as special
            if (bone.part is FingerModel)
            {
                FingerModel finger = bone.part as FingerModel;
                if (finger == finger.hand.thumb)
                {
                    slave.isSpecial = true;
                }
            }

            if (!slave.usePhysics)
            {
                Debug.LogWarning("Bone " + bone.name + " has slave representation but it won't use physics");
                return;
            }

            // Set pheasy if needed
            if (slave.pheasy == null)
            {
                Pheasy pheasy = slave.transformRef.GetComponent <Pheasy>();

                if (pheasy == null)
                {
                    pheasy = slave.transformRef.gameObject.AddComponent <Pheasy>();
                }

                pheasy.rb.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
                pheasy.rb.isKinematic            = true;

                pheasy.axis = model.axisPrefab;

                slave.pheasy = pheasy;
            }

            // Get connected body
            Rigidbody connectedBody = null;

            if (bone.parent != null)
            {
                if (bone.parent.reprs.ContainsKey(PuppetModel.key))
                {
                    PuppetReprModel parentSlave = bone.parent.reprs[PuppetModel.key] as PuppetReprModel;

                    if (parentSlave.usePhysics && !parentSlave.pheasy)
                    {
                        // Debug.LogWarning("Bone " + bone.parent.name + " has a " + PuppetModel.key + " representation and it use physics but it has no Pheasy component. Forcing initialization");
                        BoneStart(bone.parent, mainRoot);
                    }

                    if (parentSlave.pheasy)
                    {
                        connectedBody = parentSlave.pheasy.rb;
                    }
                }
                else
                {
                    Debug.LogWarning("Parent of bone " + bone.name + ", " + bone.parent.name + ", does not have a " + PuppetModel.key + " representation");
                }
            }

            // Add and save pheasy.constraint
            if (slave.constraint == null || slave.constraint.pheasy == null)
            {
                slave.constraint = slave.pheasy.AddTargetConstraint("PuppetBone", connectedBody, false, null);
            }

            // Replace or instantiate goal
            if (slave.goal == null)
            {
                slave.goal = slave.constraint.connectedAnchor;
            }
            else
            {
                slave.constraint.connectedAnchor = slave.goal;
            }

            // Set freedom
            slave.constraint.settings.angularMotion = ConfigurableJointMotion.Free;

            if (connectedBody != null)
            {
                slave.constraint.settings.linearMotion = ConfigurableJointMotion.Locked;
            }
            else
            {
                slave.constraint.settings.linearMotion = ConfigurableJointMotion.Free;
            }

            // Replace connected body by parent
            if (bone.parent == null || slave.point.bone == mainRoot || slave.constraint.connectedBody == null) // Wrist
            {
                UpdatePhysicsSettings(slave, model.configuration.root);
            }
            else if (slave.isSpecial) // Special bones
            {
                UpdatePhysicsSettings(slave, model.configuration.special);
            }
            else // Common bones
            {
                UpdatePhysicsSettings(slave, model.configuration.standard);
            }

            if (slave.constraint != null)
            {
                // Stability
                slave.constraint.settings.collideWithConnectedRb = false;
                slave.pheasy.safeMode    = true;
                slave.pheasy.gradualMode = false;

                // Forced values
                slave.constraint.enabled = true;
                slave.constraint.keepAxisRelativeToObject = true;

                slave.pheasy.rb.isKinematic            = false;
                slave.pheasy.rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;

                // Performance
                slave.pheasy.editMode = true;
                AsyncHelpers.DoAfterFrames(this, 1, () => slave.pheasy.editMode = false);
            }

            // Find colliders and triggers. Children colliders are updated before their parents
            slave.pheasy.UpdateCollidersAndTriggerNotifiers();
        }