aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs')
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs253
1 files changed, 221 insertions, 32 deletions
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
index 1734ab7..f1f94a7 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
@@ -131,6 +131,15 @@ namespace OpenSim.Region.Framework.Scenes
131 } 131 }
132 } 132 }
133 133
134 /// <summary>
135 /// This indicates whether the object has changed such that it needs to be repersisted to permenant storage
136 /// (the database).
137 /// </summary>
138 /// <remarks>
139 /// Ultimately, this should be managed such that region modules can change it at the end of a set of operations
140 /// so that either all changes are preserved or none at all. However, currently, a large amount of internal
141 /// code will set this anyway when some object properties are changed.
142 /// </remarks>
134 public bool HasGroupChanged 143 public bool HasGroupChanged
135 { 144 {
136 set 145 set
@@ -244,6 +253,22 @@ namespace OpenSim.Region.Framework.Scenes
244 } 253 }
245 } 254 }
246 255
256 /// <summary>
257 /// If this scene object has an attachment point then indicate whether there is a point where
258 /// attachments are perceivable by avatars other than the avatar to which this object is attached.
259 /// </summary>
260 /// <remarks>
261 /// HUDs are not perceivable by other avatars.
262 /// </remarks>
263 public bool HasPrivateAttachmentPoint
264 {
265 get
266 {
267 return AttachmentPoint >= (uint)OpenMetaverse.AttachmentPoint.HUDCenter2
268 && AttachmentPoint <= (uint)OpenMetaverse.AttachmentPoint.HUDBottomRight;
269 }
270 }
271
247 public void ClearPartAttachmentData() 272 public void ClearPartAttachmentData()
248 { 273 {
249 AttachmentPoint = 0; 274 AttachmentPoint = 0;
@@ -621,6 +646,9 @@ namespace OpenSim.Region.Framework.Scenes
621 return; 646 return;
622 } 647 }
623 } 648 }
649
650 // Restuff the new GroupPosition into each SOP of the linkset.
651 // This has the affect of resetting and tainting the physics actors.
624 SceneObjectPart[] parts = m_parts.GetArray(); 652 SceneObjectPart[] parts = m_parts.GetArray();
625 bool triggerScriptEvent = m_rootPart.GroupPosition != val; 653 bool triggerScriptEvent = m_rootPart.GroupPosition != val;
626 if (m_dupeInProgress) 654 if (m_dupeInProgress)
@@ -1649,16 +1677,6 @@ namespace OpenSim.Region.Framework.Scenes
1649 { 1677 {
1650 return Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); 1678 return Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f);
1651 } 1679 }
1652
1653 /// <summary>
1654 /// Added as a way for the storage provider to reset the scene,
1655 /// most likely a better way to do this sort of thing but for now...
1656 /// </summary>
1657 /// <param name="scene"></param>
1658 public void SetScene(Scene scene)
1659 {
1660 m_scene = scene;
1661 }
1662 1680
1663 /// <summary> 1681 /// <summary>
1664 /// Set a part to act as the root part for this scene object 1682 /// Set a part to act as the root part for this scene object
@@ -1740,6 +1758,9 @@ namespace OpenSim.Region.Framework.Scenes
1740 1758
1741 public void ResetChildPrimPhysicsPositions() 1759 public void ResetChildPrimPhysicsPositions()
1742 { 1760 {
1761 // Setting this SOG's absolute position also loops through and sets the positions
1762 // of the SOP's in this SOG's linkset. This has the side affect of making sure
1763 // the physics world matches the simulated world.
1743 AbsolutePosition = AbsolutePosition; // could someone in the know please explain how this works? 1764 AbsolutePosition = AbsolutePosition; // could someone in the know please explain how this works?
1744 1765
1745 // teravus: AbsolutePosition is NOT a normal property! 1766 // teravus: AbsolutePosition is NOT a normal property!
@@ -1816,8 +1837,9 @@ namespace OpenSim.Region.Framework.Scenes
1816 part.ClearUpdateSchedule(); 1837 part.ClearUpdateSchedule();
1817 if (part == m_rootPart) 1838 if (part == m_rootPart)
1818 { 1839 {
1819 if (!IsAttachment || (AttachedAvatar == avatar.ControllingClient.AgentId) || 1840 if (!IsAttachment
1820 (AttachmentPoint < 31) || (AttachmentPoint > 38)) 1841 || AttachedAvatar == avatar.ControllingClient.AgentId
1842 || !HasPrivateAttachmentPoint)
1821 avatar.ControllingClient.SendKillObject(m_regionHandle, new List<uint> { part.LocalId }); 1843 avatar.ControllingClient.SendKillObject(m_regionHandle, new List<uint> { part.LocalId });
1822 } 1844 }
1823 } 1845 }
@@ -2531,8 +2553,13 @@ namespace OpenSim.Region.Framework.Scenes
2531 } 2553 }
2532 2554
2533 /// <summary> 2555 /// <summary>
2534 /// Schedule a full update for this scene object 2556 /// Schedule a full update for this scene object to all interested viewers.
2535 /// </summary> 2557 /// </summary>
2558 /// <remarks>
2559 /// Ultimately, this should be managed such that region modules can invoke it at the end of a set of operations
2560 /// so that either all changes are sent at once. However, currently, a large amount of internal
2561 /// code will set this anyway when some object properties are changed.
2562 /// </remarks>
2536 public void ScheduleGroupForFullUpdate() 2563 public void ScheduleGroupForFullUpdate()
2537 { 2564 {
2538// if (IsAttachment) 2565// if (IsAttachment)
@@ -2551,8 +2578,13 @@ namespace OpenSim.Region.Framework.Scenes
2551 } 2578 }
2552 2579
2553 /// <summary> 2580 /// <summary>
2554 /// Schedule a terse update for this scene object 2581 /// Schedule a terse update for this scene object to all interested viewers.
2555 /// </summary> 2582 /// </summary>
2583 /// <remarks>
2584 /// Ultimately, this should be managed such that region modules can invoke it at the end of a set of operations
2585 /// so that either all changes are sent at once. However, currently, a large amount of internal
2586 /// code will set this anyway when some object properties are changed.
2587 /// </remarks>
2556 public void ScheduleGroupForTerseUpdate() 2588 public void ScheduleGroupForTerseUpdate()
2557 { 2589 {
2558// m_log.DebugFormat("[SOG]: Scheduling terse update for {0} {1}", Name, UUID); 2590// m_log.DebugFormat("[SOG]: Scheduling terse update for {0} {1}", Name, UUID);
@@ -2682,12 +2714,18 @@ namespace OpenSim.Region.Framework.Scenes
2682 /// <summary> 2714 /// <summary>
2683 /// Link the prims in a given group to this group 2715 /// Link the prims in a given group to this group
2684 /// </summary> 2716 /// </summary>
2717 /// <remarks>
2718 /// Do not call this method directly - use Scene.LinkObjects() instead to avoid races between threads.
2719 /// FIXME: There are places where scripts call these methods directly without locking. This is a potential race condition.
2720 /// </remarks>
2685 /// <param name="objectGroup">The group of prims which should be linked to this group</param> 2721 /// <param name="objectGroup">The group of prims which should be linked to this group</param>
2686 public void LinkToGroup(SceneObjectGroup objectGroup) 2722 public void LinkToGroup(SceneObjectGroup objectGroup)
2687 { 2723 {
2688 LinkToGroup(objectGroup, false); 2724 LinkToGroup(objectGroup, false);
2689 } 2725 }
2690 2726
2727 // Link an existing group to this group.
2728 // The group being linked need not be a linkset -- it can have just one prim.
2691 public void LinkToGroup(SceneObjectGroup objectGroup, bool insert) 2729 public void LinkToGroup(SceneObjectGroup objectGroup, bool insert)
2692 { 2730 {
2693// m_log.DebugFormat( 2731// m_log.DebugFormat(
@@ -2698,6 +2736,7 @@ namespace OpenSim.Region.Framework.Scenes
2698 if (objectGroup == this) 2736 if (objectGroup == this)
2699 return; 2737 return;
2700 2738
2739 // 'linkPart' == the root of the group being linked into this group
2701 SceneObjectPart linkPart = objectGroup.m_rootPart; 2740 SceneObjectPart linkPart = objectGroup.m_rootPart;
2702 2741
2703 if (m_rootPart.PhysActor != null) 2742 if (m_rootPart.PhysActor != null)
@@ -2709,31 +2748,44 @@ namespace OpenSim.Region.Framework.Scenes
2709 bool grpusephys = UsesPhysics; 2748 bool grpusephys = UsesPhysics;
2710 bool grptemporary = IsTemporary; 2749 bool grptemporary = IsTemporary;
2711 2750
2751 // Remember where the group being linked thought it was
2712 Vector3 oldGroupPosition = linkPart.GroupPosition; 2752 Vector3 oldGroupPosition = linkPart.GroupPosition;
2713 Quaternion oldRootRotation = linkPart.RotationOffset; 2753 Quaternion oldRootRotation = linkPart.RotationOffset;
2714 2754
2715 linkPart.OffsetPosition = linkPart.GroupPosition - AbsolutePosition; 2755 // A linked SOP remembers its location and rotation relative to the root of a group.
2756 // Convert the root of the group being linked to be relative to the
2757 // root of the group being linked to.
2758 // Note: Some of the assignments have complex side effects.
2716 2759
2760 // First move the new group's root SOP's position to be relative to ours
2761 // (radams1: Not sure if the multiple setting of OffsetPosition is required. If not,
2762 // this code can be reordered to have a more logical flow.)
2763 linkPart.OffsetPosition = linkPart.GroupPosition - AbsolutePosition;
2764 // Assign the new parent to the root of the old group
2717 linkPart.ParentID = m_rootPart.LocalId; 2765 linkPart.ParentID = m_rootPart.LocalId;
2718 2766 // Now that it's a child, it's group position is our root position
2719 linkPart.GroupPosition = AbsolutePosition; 2767 linkPart.GroupPosition = AbsolutePosition;
2720 2768
2721 Vector3 axPos = linkPart.OffsetPosition; 2769 Vector3 axPos = linkPart.OffsetPosition;
2770 // Rotate the linking root SOP's position to be relative to the new root prim
2722 Quaternion parentRot = m_rootPart.RotationOffset; 2771 Quaternion parentRot = m_rootPart.RotationOffset;
2723 axPos *= Quaternion.Conjugate(parentRot); 2772 axPos *= Quaternion.Conjugate(parentRot);
2724 linkPart.OffsetPosition = axPos; 2773 linkPart.OffsetPosition = axPos;
2725 2774
2775 // Make the linking root SOP's rotation relative to the new root prim
2726 Quaternion oldRot = linkPart.RotationOffset; 2776 Quaternion oldRot = linkPart.RotationOffset;
2727 Quaternion newRot = Quaternion.Conjugate(parentRot) * oldRot; 2777 Quaternion newRot = Quaternion.Conjugate(parentRot) * oldRot;
2728 linkPart.RotationOffset = newRot; 2778 linkPart.RotationOffset = newRot;
2729 2779
2730// linkPart.ParentID = m_rootPart.LocalId; done above 2780 // If there is only one SOP in a SOG, the LinkNum is zero. I.e., not a linkset.
2731 2781 // Now that we know this SOG has at least two SOPs in it, the new root
2782 // SOP becomes the first in the linkset.
2732 if (m_rootPart.LinkNum == 0) 2783 if (m_rootPart.LinkNum == 0)
2733 m_rootPart.LinkNum = 1; 2784 m_rootPart.LinkNum = 1;
2734 2785
2735 lock (m_parts.SyncRoot) 2786 lock (m_parts.SyncRoot)
2736 { 2787 {
2788 // Calculate the new link number for the old root SOP
2737 int linkNum; 2789 int linkNum;
2738 if (insert) 2790 if (insert)
2739 { 2791 {
@@ -2749,6 +2801,7 @@ namespace OpenSim.Region.Framework.Scenes
2749 linkNum = PrimCount + 1; 2801 linkNum = PrimCount + 1;
2750 } 2802 }
2751 2803
2804 // Add the old root SOP as a part in our group's list
2752 m_parts.Add(linkPart.UUID, linkPart); 2805 m_parts.Add(linkPart.UUID, linkPart);
2753 2806
2754 linkPart.SetParent(this); 2807 linkPart.SetParent(this);
@@ -2756,6 +2809,8 @@ namespace OpenSim.Region.Framework.Scenes
2756 2809
2757 // let physics know preserve part volume dtc messy since UpdatePrimFlags doesn't look to parent changes for now 2810 // let physics know preserve part volume dtc messy since UpdatePrimFlags doesn't look to parent changes for now
2758 linkPart.UpdatePrimFlags(grpusephys, grptemporary, (IsPhantom || (linkPart.Flags & PrimFlags.Phantom) != 0), linkPart.VolumeDetectActive, true); 2811 linkPart.UpdatePrimFlags(grpusephys, grptemporary, (IsPhantom || (linkPart.Flags & PrimFlags.Phantom) != 0), linkPart.VolumeDetectActive, true);
2812
2813 // If the added SOP is physical, also tell the physics engine about the link relationship.
2759 if (linkPart.PhysActor != null && m_rootPart.PhysActor != null && m_rootPart.PhysActor.IsPhysical) 2814 if (linkPart.PhysActor != null && m_rootPart.PhysActor != null && m_rootPart.PhysActor.IsPhysical)
2760 { 2815 {
2761 linkPart.PhysActor.link(m_rootPart.PhysActor); 2816 linkPart.PhysActor.link(m_rootPart.PhysActor);
@@ -2763,21 +2818,28 @@ namespace OpenSim.Region.Framework.Scenes
2763 } 2818 }
2764 2819
2765 linkPart.LinkNum = linkNum++; 2820 linkPart.LinkNum = linkNum++;
2821 linkPart.UpdatePrimFlags(UsesPhysics, IsTemporary, IsPhantom, IsVolumeDetect, false);
2766 2822
2823 // Get a list of the SOP's in the old group in order of their linknum's.
2767 SceneObjectPart[] ogParts = objectGroup.Parts; 2824 SceneObjectPart[] ogParts = objectGroup.Parts;
2768 Array.Sort(ogParts, delegate(SceneObjectPart a, SceneObjectPart b) 2825 Array.Sort(ogParts, delegate(SceneObjectPart a, SceneObjectPart b)
2769 { 2826 {
2770 return a.LinkNum - b.LinkNum; 2827 return a.LinkNum - b.LinkNum;
2771 }); 2828 });
2772 2829
2830 // Add each of the SOP's from the old linkset to our linkset
2773 for (int i = 0; i < ogParts.Length; i++) 2831 for (int i = 0; i < ogParts.Length; i++)
2774 { 2832 {
2775 SceneObjectPart part = ogParts[i]; 2833 SceneObjectPart part = ogParts[i];
2776 if (part.UUID != objectGroup.m_rootPart.UUID) 2834 if (part.UUID != objectGroup.m_rootPart.UUID)
2777 { 2835 {
2778 LinkNonRootPart(part, oldGroupPosition, oldRootRotation, linkNum++); 2836 LinkNonRootPart(part, oldGroupPosition, oldRootRotation, linkNum++);
2779 // let physics know 2837
2838 // Update the physics flags for the newly added SOP
2839 // (Is this necessary? LinkNonRootPart() has already called UpdatePrimFlags but with different flags!??)
2780 part.UpdatePrimFlags(grpusephys, grptemporary, (IsPhantom || (part.Flags & PrimFlags.Phantom) != 0), part.VolumeDetectActive, true); 2840 part.UpdatePrimFlags(grpusephys, grptemporary, (IsPhantom || (part.Flags & PrimFlags.Phantom) != 0), part.VolumeDetectActive, true);
2841
2842 // If the added SOP is physical, also tell the physics engine about the link relationship.
2781 if (part.PhysActor != null && m_rootPart.PhysActor != null && m_rootPart.PhysActor.IsPhysical) 2843 if (part.PhysActor != null && m_rootPart.PhysActor != null && m_rootPart.PhysActor.IsPhysical)
2782 { 2844 {
2783 part.PhysActor.link(m_rootPart.PhysActor); 2845 part.PhysActor.link(m_rootPart.PhysActor);
@@ -2788,6 +2850,7 @@ namespace OpenSim.Region.Framework.Scenes
2788 } 2850 }
2789 } 2851 }
2790 2852
2853 // Now that we've aquired all of the old SOG's parts, remove the old SOG from the scene.
2791 m_scene.UnlinkSceneObject(objectGroup, true); 2854 m_scene.UnlinkSceneObject(objectGroup, true);
2792 objectGroup.IsDeleted = true; 2855 objectGroup.IsDeleted = true;
2793 2856
@@ -2814,6 +2877,11 @@ namespace OpenSim.Region.Framework.Scenes
2814 /// Delink the given prim from this group. The delinked prim is established as 2877 /// Delink the given prim from this group. The delinked prim is established as
2815 /// an independent SceneObjectGroup. 2878 /// an independent SceneObjectGroup.
2816 /// </summary> 2879 /// </summary>
2880 /// <remarks>
2881 /// FIXME: This method should not be called directly since it bypasses update locking, allowing a potential race
2882 /// condition. But currently there is no
2883 /// alternative method that does take a lonk to delink a single prim.
2884 /// </remarks>
2817 /// <param name="partID"></param> 2885 /// <param name="partID"></param>
2818 /// <returns>The object group of the newly delinked prim. Null if part could not be found</returns> 2886 /// <returns>The object group of the newly delinked prim. Null if part could not be found</returns>
2819 public SceneObjectGroup DelinkFromGroup(uint partID) 2887 public SceneObjectGroup DelinkFromGroup(uint partID)
@@ -2825,6 +2893,11 @@ namespace OpenSim.Region.Framework.Scenes
2825 /// Delink the given prim from this group. The delinked prim is established as 2893 /// Delink the given prim from this group. The delinked prim is established as
2826 /// an independent SceneObjectGroup. 2894 /// an independent SceneObjectGroup.
2827 /// </summary> 2895 /// </summary>
2896 /// <remarks>
2897 /// FIXME: This method should not be called directly since it bypasses update locking, allowing a potential race
2898 /// condition. But currently there is no
2899 /// alternative method that does take a lonk to delink a single prim.
2900 /// </remarks>
2828 /// <param name="partID"></param> 2901 /// <param name="partID"></param>
2829 /// <param name="sendEvents"></param> 2902 /// <param name="sendEvents"></param>
2830 /// <returns>The object group of the newly delinked prim. Null if part could not be found</returns> 2903 /// <returns>The object group of the newly delinked prim. Null if part could not be found</returns>
@@ -2850,6 +2923,11 @@ namespace OpenSim.Region.Framework.Scenes
2850 /// Delink the given prim from this group. The delinked prim is established as 2923 /// Delink the given prim from this group. The delinked prim is established as
2851 /// an independent SceneObjectGroup. 2924 /// an independent SceneObjectGroup.
2852 /// </summary> 2925 /// </summary>
2926 /// <remarks>
2927 /// FIXME: This method should not be called directly since it bypasses update locking, allowing a potential race
2928 /// condition. But currently there is no
2929 /// alternative method that does take a lock to delink a single prim.
2930 /// </remarks>
2853 /// <param name="partID"></param> 2931 /// <param name="partID"></param>
2854 /// <param name="sendEvents"></param> 2932 /// <param name="sendEvents"></param>
2855 /// <returns>The object group of the newly delinked prim.</returns> 2933 /// <returns>The object group of the newly delinked prim.</returns>
@@ -2864,6 +2942,7 @@ namespace OpenSim.Region.Framework.Scenes
2864 2942
2865 linkPart.ClearUndoState(); 2943 linkPart.ClearUndoState();
2866 2944
2945 Vector3 worldPos = linkPart.GetWorldPosition();
2867 Quaternion worldRot = linkPart.GetWorldRotation(); 2946 Quaternion worldRot = linkPart.GetWorldRotation();
2868 2947
2869 // Remove the part from this object 2948 // Remove the part from this object
@@ -2873,6 +2952,7 @@ namespace OpenSim.Region.Framework.Scenes
2873 2952
2874 SceneObjectPart[] parts = m_parts.GetArray(); 2953 SceneObjectPart[] parts = m_parts.GetArray();
2875 2954
2955 // Rejigger the linknum's of the remaining SOP's to fill any gap
2876 if (parts.Length == 1 && RootPart != null) 2956 if (parts.Length == 1 && RootPart != null)
2877 { 2957 {
2878 // Single prim left 2958 // Single prim left
@@ -2894,22 +2974,31 @@ namespace OpenSim.Region.Framework.Scenes
2894 2974
2895 PhysicsActor linkPartPa = linkPart.PhysActor; 2975 PhysicsActor linkPartPa = linkPart.PhysActor;
2896 2976
2977 // Remove the SOP from the physical scene.
2978 // If the new SOG is physical, it is re-created later.
2979 // (There is a problem here in that we have not yet told the physics
2980 // engine about the delink. Someday, linksets should be made first
2981 // class objects in the physics engine interface).
2897 if (linkPartPa != null) 2982 if (linkPartPa != null)
2898 m_scene.PhysicsScene.RemovePrim(linkPartPa); 2983 m_scene.PhysicsScene.RemovePrim(linkPartPa);
2899 2984
2900 // We need to reset the child part's position 2985 // We need to reset the child part's position
2901 // ready for life as a separate object after being a part of another object 2986 // ready for life as a separate object after being a part of another object
2902 Quaternion parentRot = m_rootPart.RotationOffset;
2903 2987
2988 /* This commented out code seems to recompute what GetWorldPosition already does.
2989 * Replace with a call to GetWorldPosition (before unlinking)
2990 Quaternion parentRot = m_rootPart.RotationOffset;
2904 Vector3 axPos = linkPart.OffsetPosition; 2991 Vector3 axPos = linkPart.OffsetPosition;
2905
2906 axPos *= parentRot; 2992 axPos *= parentRot;
2907 linkPart.OffsetPosition = new Vector3(axPos.X, axPos.Y, axPos.Z); 2993 linkPart.OffsetPosition = new Vector3(axPos.X, axPos.Y, axPos.Z);
2908 linkPart.GroupPosition = AbsolutePosition + linkPart.OffsetPosition; 2994 linkPart.GroupPosition = AbsolutePosition + linkPart.OffsetPosition;
2909 linkPart.OffsetPosition = new Vector3(0, 0, 0); 2995 linkPart.OffsetPosition = new Vector3(0, 0, 0);
2910 2996 */
2997 linkPart.GroupPosition = worldPos;
2998 linkPart.OffsetPosition = Vector3.Zero;
2911 linkPart.RotationOffset = worldRot; 2999 linkPart.RotationOffset = worldRot;
2912 3000
3001 // Create a new SOG to go around this unlinked and unattached SOP
2913 SceneObjectGroup objectGroup = new SceneObjectGroup(linkPart); 3002 SceneObjectGroup objectGroup = new SceneObjectGroup(linkPart);
2914 3003
2915 m_scene.AddNewSceneObject(objectGroup, true); 3004 m_scene.AddNewSceneObject(objectGroup, true);
@@ -2947,42 +3036,58 @@ namespace OpenSim.Region.Framework.Scenes
2947 m_isBackedUp = false; 3036 m_isBackedUp = false;
2948 } 3037 }
2949 3038
3039 // This links an SOP from a previous linkset into my linkset.
3040 // The trick is that the SOP's position and rotation are relative to the old root SOP's
3041 // so we are passed in the position and rotation of the old linkset so this can
3042 // unjigger this SOP's position and rotation from the previous linkset and
3043 // then make them relative to my linkset root.
2950 private void LinkNonRootPart(SceneObjectPart part, Vector3 oldGroupPosition, Quaternion oldGroupRotation, int linkNum) 3044 private void LinkNonRootPart(SceneObjectPart part, Vector3 oldGroupPosition, Quaternion oldGroupRotation, int linkNum)
2951 { 3045 {
2952 Quaternion parentRot = oldGroupRotation; 3046 Quaternion parentRot = oldGroupRotation;
2953 Quaternion oldRot = part.RotationOffset; 3047 Quaternion oldRot = part.RotationOffset;
2954 Quaternion worldRot = parentRot * oldRot;
2955
2956 parentRot = oldGroupRotation;
2957 3048
3049 // Move our position to not be relative to the old parent
2958 Vector3 axPos = part.OffsetPosition; 3050 Vector3 axPos = part.OffsetPosition;
2959
2960 axPos *= parentRot; 3051 axPos *= parentRot;
2961 part.OffsetPosition = axPos; 3052 part.OffsetPosition = axPos;
2962 Vector3 newPos = oldGroupPosition + part.OffsetPosition; 3053 Vector3 newPos = oldGroupPosition + part.OffsetPosition;
2963 part.GroupPosition = newPos; 3054 part.GroupPosition = newPos;
2964 part.OffsetPosition = Vector3.Zero; 3055 part.OffsetPosition = Vector3.Zero;
3056
3057 // Compution our rotation to be not relative to the old parent
3058 Quaternion worldRot = parentRot * oldRot;
2965 part.RotationOffset = worldRot; 3059 part.RotationOffset = worldRot;
2966 3060
3061 // Add this SOP to our linkset
2967 part.SetParent(this); 3062 part.SetParent(this);
2968 part.ParentID = m_rootPart.LocalId; 3063 part.ParentID = m_rootPart.LocalId;
2969
2970 m_parts.Add(part.UUID, part); 3064 m_parts.Add(part.UUID, part);
2971 3065
2972 part.LinkNum = linkNum; 3066 part.LinkNum = linkNum;
2973 3067
2974 part.OffsetPosition = newPos - AbsolutePosition; 3068 // Compute the new position of this SOP relative to the group position
3069 part.OffsetPosition = part.GroupPosition - AbsolutePosition;
2975 3070
2976 Quaternion rootRotation = m_rootPart.RotationOffset; 3071 // (radams1 20120711: I don't know why part.OffsetPosition is set multiple times.
3072 // It would have the affect of setting the physics engine position multiple
3073 // times. In theory, that is not necessary but I don't have a good linkset
3074 // test to know that cleaning up this code wouldn't break things.)
2977 3075
3076 // Rotate the relative position by the rotation of the group
3077 Quaternion rootRotation = m_rootPart.RotationOffset;
2978 Vector3 pos = part.OffsetPosition; 3078 Vector3 pos = part.OffsetPosition;
2979 pos *= Quaternion.Conjugate(rootRotation); 3079 pos *= Quaternion.Conjugate(rootRotation);
2980 part.OffsetPosition = pos; 3080 part.OffsetPosition = pos;
2981 3081
3082 // Compute the SOP's rotation relative to the rotation of the group.
2982 parentRot = m_rootPart.RotationOffset; 3083 parentRot = m_rootPart.RotationOffset;
2983 oldRot = part.RotationOffset; 3084 oldRot = part.RotationOffset;
2984 Quaternion newRot = Quaternion.Conjugate(parentRot) * worldRot; 3085 Quaternion newRot = Quaternion.Conjugate(parentRot) * worldRot;
2985 part.RotationOffset = newRot; 3086 part.RotationOffset = newRot;
3087
3088 // Since this SOP's state has changed, push those changes into the physics engine
3089 // and the simulator.
3090 part.UpdatePrimFlags(UsesPhysics, IsTemporary, IsPhantom, IsVolumeDetect, false);
2986 } 3091 }
2987 3092
2988 /// <summary> 3093 /// <summary>
@@ -3498,7 +3603,7 @@ namespace OpenSim.Region.Framework.Scenes
3498 3603
3499 //we need to do a terse update even if the move wasn't allowed 3604 //we need to do a terse update even if the move wasn't allowed
3500 // so that the position is reset in the client (the object snaps back) 3605 // so that the position is reset in the client (the object snaps back)
3501 ScheduleGroupForTerseUpdate(); 3606 RootPart.ScheduleTerseUpdate();
3502 } 3607 }
3503 3608
3504 /// <summary> 3609 /// <summary>
@@ -3613,6 +3718,11 @@ namespace OpenSim.Region.Framework.Scenes
3613 m_scene.PhysicsScene.AddPhysicsActorTaint(actor); 3718 m_scene.PhysicsScene.AddPhysicsActorTaint(actor);
3614 } 3719 }
3615 3720
3721 if (IsAttachment)
3722 {
3723 m_rootPart.AttachedPos = pos;
3724 }
3725
3616 AbsolutePosition = pos; 3726 AbsolutePosition = pos;
3617 3727
3618 HasGroupChanged = true; 3728 HasGroupChanged = true;
@@ -4199,7 +4309,86 @@ namespace OpenSim.Region.Framework.Scenes
4199 for (int i = 0; i < parts.Length; i++) 4309 for (int i = 0; i < parts.Length; i++)
4200 parts[i].TriggerScriptChangedEvent(val); 4310 parts[i].TriggerScriptChangedEvent(val);
4201 } 4311 }
4202 4312
4313 /// <summary>
4314 /// Returns a count of the number of scripts in this groups parts.
4315 /// </summary>
4316 public int ScriptCount()
4317 {
4318 int count = 0;
4319 SceneObjectPart[] parts = m_parts.GetArray();
4320 for (int i = 0; i < parts.Length; i++)
4321 count += parts[i].Inventory.ScriptCount();
4322
4323 return count;
4324 }
4325
4326 /// <summary>
4327 /// A float the value is a representative execution time in milliseconds of all scripts in the link set.
4328 /// </summary>
4329 public float ScriptExecutionTime()
4330 {
4331 IScriptModule[] engines = Scene.RequestModuleInterfaces<IScriptModule>();
4332
4333 if (engines.Length == 0) // No engine at all
4334 return 0.0f;
4335
4336 float time = 0.0f;
4337
4338 // get all the scripts in all parts
4339 SceneObjectPart[] parts = m_parts.GetArray();
4340 List<TaskInventoryItem> scripts = new List<TaskInventoryItem>();
4341 for (int i = 0; i < parts.Length; i++)
4342 {
4343 scripts.AddRange(parts[i].Inventory.GetInventoryItems(InventoryType.LSL));
4344 }
4345 // extract the UUIDs
4346 List<UUID> ids = new List<UUID>(scripts.Count);
4347 foreach (TaskInventoryItem script in scripts)
4348 {
4349 if (!ids.Contains(script.ItemID))
4350 {
4351 ids.Add(script.ItemID);
4352 }
4353 }
4354 // Offer the list of script UUIDs to each engine found and accumulate the time
4355 foreach (IScriptModule e in engines)
4356 {
4357 if (e != null)
4358 {
4359 time += e.GetScriptExecutionTime(ids);
4360 }
4361 }
4362 return time;
4363 }
4364
4365 /// <summary>
4366 /// Returns a count of the number of running scripts in this groups parts.
4367 /// </summary>
4368 public int RunningScriptCount()
4369 {
4370 int count = 0;
4371 SceneObjectPart[] parts = m_parts.GetArray();
4372 for (int i = 0; i < parts.Length; i++)
4373 count += parts[i].Inventory.RunningScriptCount();
4374
4375 return count;
4376 }
4377
4378 /// <summary>
4379 /// Gets the number of sitting avatars.
4380 /// </summary>
4381 /// <remarks>This applies to all sitting avatars whether there is a sit target set or not.</remarks>
4382 /// <returns></returns>
4383 public int GetSittingAvatarsCount()
4384 {
4385 int count = 0;
4386
4387 Array.ForEach<SceneObjectPart>(m_parts.GetArray(), p => count += p.GetSittingAvatarsCount());
4388
4389 return count;
4390 }
4391
4203 public override string ToString() 4392 public override string ToString()
4204 { 4393 {
4205 return String.Format("{0} {1} ({2})", Name, UUID, AbsolutePosition); 4394 return String.Format("{0} {1} ({2})", Name, UUID, AbsolutePosition);