private void MarkChildRetain(IBone parent) { foreach (var bone in m_BoneList.bones) { if (BoneUtility.IsOffspringOf(bone, parent) || bone == parent) { bone.markedRetainWorldPosition = true; } } }
public void DeleteBone(IBone bone) { foreach (var otherBone in m_BoneList.bones) { if (BoneUtility.IsOffspringOf(otherBone, bone)) { throw new InvalidOperationException("Cannot delete a parent bone with children. Children should all be removed/transfered first."); } } m_BoneList.bones.Remove((Bone)bone); m_OrderDirty = true; SetDirty(); MarkChildRetain(bone); UpdateHierarchyFromBone(bone, true); }
public void Parent(IBone newChild, IBone parent) { if (newChild == parent) { throw new InvalidOperationException("Cannot parent a bone to itself."); } if (BoneUtility.IsOffspringOf(parent, newChild)) { throw new InvalidOperationException(string.Format("Cannot parent {0} to {1}. This will create a loop.", newChild.name, parent.name)); } MarkChildRetain(newChild); ((Bone)newChild).parent = parent; m_OrderDirty = true; SetDirty(); UpdateHierarchyFromBone(newChild, true); }
private void UpdateHierarchyFromBone(IBone fromBone, bool keepChildPosition) { if (m_OrderDirty) { m_OrderDirty = false; SortBones(); } foreach (var bone in m_BoneList.bones) { if (BoneUtility.IsOffspringOf(bone, fromBone) || bone == fromBone) { if (bone.markedRetainWorldPosition) { bone.RestoreWorldPosition(); } bone.RecalculateMatrix(); } } }
// Click on bone body and tip. Handle parenting state here as well. private void HandleSelection() { // Tip first. If a bone near the tip is clicked later, the bone will be selected instead of tip. foreach (var bone in m_Model.bones) { if (m_View.HandleTipSelect(bone)) { SelectSingleBone(bone); m_View.Refresh(); // Always reset relationship after a new selection. ResetRelationship(); m_InfoView.SelectionChanged(); break; } } // Bone second. Replace selected tip, if bone is clicked on as well. foreach (var bone in m_Model.bones) { if (m_View.HandleBoneSelect(bone)) { // During parenting state, a click on a bone will not select the bone, but mark this bone as new parent. if (state.parenting) { foreach (var selectedBone in state.selectedBones) { if (bone == selectedBone) { throw new InvalidOperationException("Cannot parent a bone to itself."); } if (BoneUtility.IsOffspringOf(bone, selectedBone)) { throw new InvalidOperationException(string.Format("Cannot parent {0} to {1}. This will create a loop.", bone.name, selectedBone.name)); } } RecordUndo(bone, "bone parent"); foreach (var selectedBone in state.selectedBones) { m_Model.Parent(selectedBone, bone); } // Exit parenting state. state.parenting = false; m_View.Refresh(); break; } else { if (state.multiselecting) { state.selectedBones.Add(bone); } else if (!state.selectedBones.Contains(bone)) { SelectSingleBone(bone); } m_View.Refresh(); // Always reset relationship after a new selection. ResetRelationship(); m_InfoView.SelectionChanged(); if (!state.multiselecting) { break; } } } } }
// Repopulate the relationship hash tables for current selected bones. public void ResetRelationship() { m_ChildrenOfSelected.Clear(); m_SiblingToMoveTogether.Clear(); m_UpdateParentTip.Clear(); m_OffspringOfSelected.Clear(); m_OffspringOfParent.Clear(); foreach (var bone in state.selectedBones) { List <IBone> childrenOfSelected = new List <IBone>(); List <IBone> childrenToMoveTogether = new List <IBone>(); List <IBone> siblingToMoveTogether = new List <IBone>(); List <IBone> offspringOfSelected = new List <IBone>(); List <IBone> offspringOfParent = new List <IBone>(); // Traverse all bones to find relationships. foreach (var otherBone in m_Model.bones) { // Itself if (otherBone == bone) { if (!bone.isRoot) { // Add itself as offspring of its parent. Made sense, right? offspringOfParent.Add(otherBone); } continue; } // Immediate child. if (otherBone.parent == bone) { childrenOfSelected.Add(otherBone); if (ShouldSnap(bone.position, otherBone.position)) { // If close enough, they should move together in normal movement. childrenToMoveTogether.Add(otherBone); } } // Sibling. if (otherBone.parent == bone.parent) { if (ShouldSnap(bone.position, otherBone.position)) { // If close enough, they should move together in normal movement. siblingToMoveTogether.Add(otherBone); } } // Child, grandchild, grand(^n)child. if (BoneUtility.IsOffspringOf(otherBone, bone)) { offspringOfSelected.Add(otherBone); } // Parent's child, grandchild, grand(^n)child. if (!bone.isRoot && BoneUtility.IsOffspringOf(otherBone, bone.parent)) { offspringOfParent.Add(otherBone); } } // If bone is close to parent's tip, it will update parent's tip position when moved (normal move). var updateParentTip = !bone.isRoot && ShouldSnap(bone.position, bone.parent.tip); m_OffspringOfSelected.Add(bone, offspringOfSelected); m_ChildrenOfSelected.Add(bone, childrenOfSelected); m_SiblingToMoveTogether.Add(bone, siblingToMoveTogether); m_UpdateParentTip.Add(bone, updateParentTip); m_OffspringOfParent.Add(bone, offspringOfParent); } // When multiple bones are selected, movement handling of offspring is disabled if ancestor is selected (normal move). m_ValidToMultipleSelectMove = new List <IBone>(); foreach (var bone in state.selectedBones) { var offspringOfOtherSelected = false; foreach (var c in m_OffspringOfSelected) { if (c.Value.Contains(bone)) { offspringOfOtherSelected = true; break; } } if (!offspringOfOtherSelected) { m_ValidToMultipleSelectMove.Add(bone); } } }