From d604cd284efc61af4616506830ff17ee52ff6e48 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Wed, 29 Apr 2009 15:54:16 +0000 Subject: [PATCH] Again, completely revamp the unlink code to finally allow unlinking arbitrary combinations of root and child prims from one or multiple link sets. Please test throughly and consider things UNSTABLE until this is proven out. --- OpenSim/Region/Framework/Scenes/SceneGraph.cs | 172 +++++++++--------- .../Framework/Scenes/SceneObjectGroup.cs | 119 ++++++------ 2 files changed, 149 insertions(+), 142 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 1e6955a795..4b4461f6e7 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -1588,107 +1588,109 @@ namespace OpenSim.Region.Framework.Scenes protected internal void DelinkObjects(List primIds, bool sendEvents) { - SceneObjectGroup parentPrim = null; - // Find the root prim among the prim ids we've been given - for (int i = 0; i < primIds.Count; i++) + List childParts = new List(); + List rootParts = new List(); + List affectedGroups = new List(); + // Look them all up in one go, since that is comparatively expensive + // + foreach (uint primID in primIds) { - // Get the group for this prim and check that it is the parent - parentPrim = GetGroupByPrim(primIds[i]); - if (parentPrim != null && parentPrim.LocalId == primIds[i]) + SceneObjectPart part = m_parentScene.GetSceneObjectPart(primID); + if (part != null) { - primIds.RemoveAt(i); - break; - } - } + if (part.LinkNum < 2) // Root or single + rootParts.Add(part); + else + childParts.Add(part); - if (parentPrim != null) - { - foreach (uint childPrimId in primIds) - { - parentPrim.DelinkFromGroup(childPrimId, sendEvents); - } - - if (parentPrim.Children.Count == 1) - { - // The link set has been completely torn down - // This is the case if you select a link set and delink - // - parentPrim.RootPart.LinkNum = 0; - if (sendEvents) - parentPrim.TriggerScriptChangedEvent(Changed.LINK); + SceneObjectGroup group = part.ParentGroup; + if (!affectedGroups.Contains(group)) + affectedGroups.Add(group); } else { - // The link set has prims remaining. This path is taken - // when a subset of a link set's prims are selected - // and the root prim is part of that selection - // - List parts = new List(parentPrim.Children.Values); - - List unlink_ids = new List(); - foreach (SceneObjectPart unlink_part in parts) - unlink_ids.Add(unlink_part.LocalId); - - // Tear down the remaining link set - // - if (unlink_ids.Count == 2) - { - DelinkObjects(unlink_ids, true); - return; - } - - DelinkObjects(unlink_ids, false); - - // Send event to root prim, then we're done with it - parentPrim.TriggerScriptChangedEvent(Changed.LINK); - - unlink_ids.Remove(parentPrim.RootPart.LocalId); - - foreach (uint localId in unlink_ids) - { - SceneObjectPart nr = GetSceneObjectPart(localId); - nr.UpdateFlag = 0; - } - - uint newRoot = unlink_ids[0]; - unlink_ids.Remove(newRoot); - - LinkObjects(null, newRoot, unlink_ids); + m_log.ErrorFormat("Viewer requested unlink of nonexistent part {0}", primID); } } - else + + foreach (SceneObjectPart child in childParts) { - // The selected prims were all child prims. Edit linked parts - // without the root prim selected will get us here + // Unlink all child parts from their groups // - List parentGroups = new List(); + child.ParentGroup.DelinkFromGroup(child, sendEvents); + } - // If the first scan failed, we need to do a /deep/ scan of the linkages. This is /really/ slow - // We know that this is not the root prim now essentially, so we don't have to worry about remapping - // which one is the root prim - bool delinkedSomething = false; - for (int i = 0; i < primIds.Count; i++) + foreach (SceneObjectPart root in rootParts) + { + // In most cases, this will run only one time, and the prim + // will be a solo prim + // However, editing linked parts and unlinking may be different + // + SceneObjectGroup group = root.ParentGroup; + List newSet = new List(group.Children.Values); + int numChildren = group.Children.Count; + + // If there are prims left in a link set, but the root is + // slated for unlink, we need to do this + // + if (numChildren != 1) { - SceneObjectGroup parent = GetGroupByPrim(primIds[i]); - parent.DelinkFromGroup(primIds[i]); - delinkedSomething = true; - if (!parentGroups.Contains(parent)) - parentGroups.Add(parent); - } - if (!delinkedSomething) - { - m_log.InfoFormat("[SCENE]: " + - "DelinkObjects(): Could not find a root prim out of {0} as given to a delink request!", - primIds); - } - else - { - foreach (SceneObjectGroup g in parentGroups) + // Unlink the remaining set + // + bool sendEventsToRemainder = true; + if (numChildren > 1) + sendEventsToRemainder = false; + + foreach (SceneObjectPart p in newSet) { - g.TriggerScriptChangedEvent(Changed.LINK); + if (p != group.RootPart) + group.DelinkFromGroup(p, sendEventsToRemainder); + } + + // If there is more than one prim remaining, we + // need to re-link + // + if (numChildren > 2) + { + // Remove old root + // + if (newSet.Contains(root)) + newSet.Remove(root); + + // Preserve link ordering + // + newSet.Sort(delegate (SceneObjectPart a, SceneObjectPart b) + { + return a.LinkNum.CompareTo(b.LinkNum); + }); + + // Determine new root + // + SceneObjectPart newRoot = newSet[0]; + newSet.RemoveAt(0); + + List linkIDs = new List(); + + foreach (SceneObjectPart newChild in newSet) + { + newChild.UpdateFlag = 0; + linkIDs.Add(newChild.LocalId); + } + + LinkObjects(null, newRoot.LocalId, linkIDs); + if (!affectedGroups.Contains(newRoot.ParentGroup)) + affectedGroups.Add(newRoot.ParentGroup); } } } + + // Finally, trigger events in the roots + // + foreach (SceneObjectGroup g in affectedGroups) + { + g.TriggerScriptChangedEvent(Changed.LINK); + g.ScheduleGroupForFullUpdate(); + } } protected internal void MakeObjectSearchable(IClientAPI remoteClient, bool IncludeInSearch, uint localID) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index f8b448383a..6dc331b1b9 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -2237,64 +2237,9 @@ namespace OpenSim.Region.Framework.Scenes { SceneObjectPart linkPart = GetChildPart(partID); - if (null != linkPart) + if (linkPart != null) { - linkPart.ClearUndoState(); -// m_log.DebugFormat( -// "[SCENE OBJECT GROUP]: Delinking part {0}, {1} from group with root part {2}, {3}", -// linkPart.Name, linkPart.UUID, RootPart.Name, RootPart.UUID); - - Quaternion worldRot = linkPart.GetWorldRotation(); - - // Remove the part from this object - lock (m_parts) - { - m_parts.Remove(linkPart.UUID); - } - - if (m_parts.Count == 1 && RootPart != null) //Single prim is left - RootPart.LinkNum = 0; - else - { - foreach (SceneObjectPart p in m_parts.Values) - { - if (p.LinkNum > linkPart.LinkNum) - p.LinkNum--; - } - } - - linkPart.ParentID = 0; - linkPart.LinkNum = 0; - - if (linkPart.PhysActor != null) - { - m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor); - } - - // We need to reset the child part's position - // ready for life as a separate object after being a part of another object - Quaternion parentRot = m_rootPart.RotationOffset; - - Vector3 axPos = linkPart.OffsetPosition; - - axPos *= parentRot; - linkPart.OffsetPosition = new Vector3(axPos.X, axPos.Y, axPos.Z); - linkPart.GroupPosition = AbsolutePosition + linkPart.OffsetPosition; - linkPart.OffsetPosition = new Vector3(0, 0, 0); - - linkPart.RotationOffset = worldRot; - - SceneObjectGroup objectGroup = new SceneObjectGroup(linkPart); - - m_scene.AddNewSceneObject(objectGroup, true); - - if (sendEvents) - linkPart.TriggerScriptChangedEvent(Changed.LINK); - - linkPart.Rezzed = RootPart.Rezzed; - - HasGroupChanged = true; - ScheduleGroupForFullUpdate(); + DelinkFromGroup(linkPart, sendEvents); } else { @@ -2304,6 +2249,66 @@ namespace OpenSim.Region.Framework.Scenes } } + public void DelinkFromGroup(SceneObjectPart linkPart, bool sendEvents) + { + linkPart.ClearUndoState(); +// m_log.DebugFormat( +// "[SCENE OBJECT GROUP]: Delinking part {0}, {1} from group with root part {2}, {3}", +// linkPart.Name, linkPart.UUID, RootPart.Name, RootPart.UUID); + + Quaternion worldRot = linkPart.GetWorldRotation(); + + // Remove the part from this object + lock (m_parts) + { + m_parts.Remove(linkPart.UUID); + } + + if (m_parts.Count == 1 && RootPart != null) //Single prim is left + RootPart.LinkNum = 0; + else + { + foreach (SceneObjectPart p in m_parts.Values) + { + if (p.LinkNum > linkPart.LinkNum) + p.LinkNum--; + } + } + + linkPart.ParentID = 0; + linkPart.LinkNum = 0; + + if (linkPart.PhysActor != null) + { + m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor); + } + + // We need to reset the child part's position + // ready for life as a separate object after being a part of another object + Quaternion parentRot = m_rootPart.RotationOffset; + + Vector3 axPos = linkPart.OffsetPosition; + + axPos *= parentRot; + linkPart.OffsetPosition = new Vector3(axPos.X, axPos.Y, axPos.Z); + linkPart.GroupPosition = AbsolutePosition + linkPart.OffsetPosition; + linkPart.OffsetPosition = new Vector3(0, 0, 0); + + linkPart.RotationOffset = worldRot; + + SceneObjectGroup objectGroup = new SceneObjectGroup(linkPart); + + m_scene.AddNewSceneObject(objectGroup, true); + + if (sendEvents) + linkPart.TriggerScriptChangedEvent(Changed.LINK); + + linkPart.Rezzed = RootPart.Rezzed; + + HasGroupChanged = true; + ScheduleGroupForFullUpdate(); + } + /// /// Stop this object from being persisted over server restarts. ///