// This links an SOP from a previous linkset into my linkset. // The trick is that the SOP's position and rotation are relative to the old root SOP's // so we are passed in the position and rotation of the old linkset so this can // unjigger this SOP's position and rotation from the previous linkset and // then make them relative to my linkset root. private void LinkNonRootPart(SceneObjectPart part, Vector3 oldGroupPosition, Quaternion oldGroupRotation, int linkNum) { Quaternion parentRot = oldGroupRotation; Quaternion oldRot = part.RotationOffset; // Move our position in world Vector3 axPos = part.OffsetPosition; axPos *= parentRot; Vector3 newPos = oldGroupPosition + axPos; part.setGroupPosition(newPos); part.setOffsetPosition(Vector3.Zero); // Compution our rotation in world Quaternion worldRot = parentRot * oldRot; part.RotationOffset = worldRot; // Add this SOP to our linkset part.SetParent(this); part.ParentID = m_rootPart.LocalId; m_parts.Add(part.UUID, part); part.LinkNum = linkNum; m_scene.updateScenePartGroup(part, this); // Compute the new position of this SOP relative to the group position part.setOffsetPosition(newPos - AbsolutePosition); // (radams1 20120711: I don't know why part.OffsetPosition is set multiple times. // It would have the affect of setting the physics engine position multiple // times. In theory, that is not necessary but I don't have a good linkset // test to know that cleaning up this code wouldn't break things.) // Compute the SOP's rotation relative to the rotation of the group. parentRot = m_rootPart.RotationOffset; oldRot = part.RotationOffset; Quaternion newRot = Quaternion.Conjugate(parentRot) * worldRot; part.setRotationOffset(newRot); Vector3 pos = part.OffsetPosition; pos *= Quaternion.Conjugate(parentRot); part.OffsetPosition = pos; // update position and orientation on physics also // Since this SOP's state has changed, push those changes into the physics engine // and the simulator. // done on caller // part.UpdatePrimFlags(UsesPhysics, IsTemporary, IsPhantom, IsVolumeDetect, false); }
/// <summary> /// Delink the given prim from this group. The delinked prim is established as /// an independent SceneObjectGroup. /// </summary> /// <remarks> /// FIXME: This method should not be called directly since it bypasses update locking, allowing a potential race /// condition. But currently there is no /// alternative method that does take a lock to delink a single prim. /// </remarks> /// <param name="partID"></param> /// <param name="sendEvents"></param> /// <returns>The object group of the newly delinked prim.</returns> public SceneObjectGroup DelinkFromGroup(SceneObjectPart linkPart, bool sendEvents) { // 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); if (m_rootPart.PhysActor != null) m_rootPart.PhysActor.Building = true; linkPart.ClearUndoState(); Vector3 worldPos = linkPart.GetWorldPosition(); Quaternion worldRot = linkPart.GetWorldRotation(); // Remove the part from this object lock (m_parts.SyncRoot) { m_parts.Remove(linkPart.UUID); SceneObjectPart[] parts = m_parts.GetArray(); // Rejigger the linknum's of the remaining SOP's to fill any gap if (parts.Length == 1 && RootPart != null) { // Single prim left RootPart.LinkNum = 0; } else { for (int i = 0; i < parts.Length; i++) { SceneObjectPart part = parts[i]; if (part.LinkNum > linkPart.LinkNum) part.LinkNum--; } } } linkPart.ParentID = 0; linkPart.LinkNum = 0; PhysicsActor linkPartPa = linkPart.PhysActor; // Remove the SOP from the physical scene. // If the new SOG is physical, it is re-created later. // (There is a problem here in that we have not yet told the physics // engine about the delink. Someday, linksets should be made first // class objects in the physics engine interface). if (linkPartPa != null) m_scene.PhysicsScene.RemovePrim(linkPartPa); // We need to reset the child part's position // ready for life as a separate object after being a part of another object /* This commented out code seems to recompute what GetWorldPosition already does. * Replace with a call to GetWorldPosition (before unlinking) 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.setGroupPosition(worldPos); linkPart.setOffsetPosition(Vector3.Zero); linkPart.setRotationOffset(worldRot); // Create a new SOG to go around this unlinked and unattached SOP SceneObjectGroup objectGroup = new SceneObjectGroup(linkPart); m_scene.AddNewSceneObject(objectGroup, true); linkPart.Rezzed = RootPart.Rezzed; // When we delete a group, we currently have to force persist to the database if the object id has changed // (since delete works by deleting all rows which have a given object id) // this is as it seems to be in sl now if(linkPart.PhysicsShapeType == (byte)PhysShapeType.none) linkPart.PhysicsShapeType = linkPart.DefaultPhysicsShapeType(); // root prims can't have type none for now if (m_rootPart.PhysActor != null) m_rootPart.PhysActor.Building = false; objectGroup.HasGroupChangedDueToDelink = true; InvalidBoundsRadius(); if (sendEvents) linkPart.TriggerScriptChangedEvent(Changed.LINK); return objectGroup; }