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.cs3940
1 files changed, 3940 insertions, 0 deletions
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
new file mode 100644
index 0000000..866a43c
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
@@ -0,0 +1,3940 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.ComponentModel;
30using System.Collections.Generic;
31using System.Drawing;
32using System.IO;
33using System.Linq;
34using System.Threading;
35using System.Xml;
36using System.Xml.Serialization;
37using OpenMetaverse;
38using OpenMetaverse.Packets;
39using OpenSim.Framework;
40using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Physics.Manager;
42using OpenSim.Region.Framework.Scenes.Serialization;
43using PermissionMask = OpenSim.Framework.PermissionMask;
44
45namespace OpenSim.Region.Framework.Scenes
46{
47 [Flags]
48 public enum scriptEvents
49 {
50 None = 0,
51 attach = 1,
52 collision = 16,
53 collision_end = 32,
54 collision_start = 64,
55 control = 128,
56 dataserver = 256,
57 email = 512,
58 http_response = 1024,
59 land_collision = 2048,
60 land_collision_end = 4096,
61 land_collision_start = 8192,
62 at_target = 16384,
63 at_rot_target = 16777216,
64 listen = 32768,
65 money = 65536,
66 moving_end = 131072,
67 moving_start = 262144,
68 not_at_rot_target = 524288,
69 not_at_target = 1048576,
70 remote_data = 8388608,
71 run_time_permissions = 268435456,
72 state_entry = 1073741824,
73 state_exit = 2,
74 timer = 4,
75 touch = 8,
76 touch_end = 536870912,
77 touch_start = 2097152,
78 transaction_result = 33554432,
79 object_rez = 4194304
80 }
81
82 struct scriptPosTarget
83 {
84 public Vector3 targetPos;
85 public float tolerance;
86 public uint handle;
87 }
88
89 struct scriptRotTarget
90 {
91 public Quaternion targetRot;
92 public float tolerance;
93 public uint handle;
94 }
95
96 public delegate void PrimCountTaintedDelegate();
97
98 /// <summary>
99 /// A scene object group is conceptually an object in the scene. The object is constituted of SceneObjectParts
100 /// (often known as prims), one of which is considered the root part.
101 /// </summary>
102 public partial class SceneObjectGroup : EntityBase, ISceneObject
103 {
104 // Axis selection bitmask used by SetAxisRotation()
105 // Just happen to be the same bits used by llSetStatus() and defined in ScriptBaseClass.
106 public enum axisSelect : int
107 {
108 STATUS_ROTATE_X = 0x002,
109 STATUS_ROTATE_Y = 0x004,
110 STATUS_ROTATE_Z = 0x008,
111 }
112
113 // This flag has the same purpose as InventoryItemFlags.ObjectSlamPerm
114 public static readonly uint SLAM = 16;
115
116 // private PrimCountTaintedDelegate handlerPrimCountTainted = null;
117
118 /// <summary>
119 /// Signal whether the non-inventory attributes of any prims in the group have changed
120 /// since the group's last persistent backup
121 /// </summary>
122 private bool m_hasGroupChanged = false;
123 private long timeFirstChanged;
124 private long timeLastChanged;
125
126 /// <summary>
127 /// This indicates whether the object has changed such that it needs to be repersisted to permenant storage
128 /// (the database).
129 /// </summary>
130 /// <remarks>
131 /// Ultimately, this should be managed such that region modules can change it at the end of a set of operations
132 /// so that either all changes are preserved or none at all. However, currently, a large amount of internal
133 /// code will set this anyway when some object properties are changed.
134 /// </remarks>
135 public bool HasGroupChanged
136 {
137 set
138 {
139 if (value)
140 {
141 timeLastChanged = DateTime.Now.Ticks;
142 if (!m_hasGroupChanged)
143 timeFirstChanged = DateTime.Now.Ticks;
144 }
145 m_hasGroupChanged = value;
146
147// m_log.DebugFormat(
148// "[SCENE OBJECT GROUP]: HasGroupChanged set to {0} for {1} {2}", m_hasGroupChanged, Name, LocalId);
149 }
150
151 get { return m_hasGroupChanged; }
152 }
153
154 private bool m_groupContainsForeignPrims = false;
155
156 /// <summary>
157 /// Whether the group contains prims that came from a different group. This happens when
158 /// linking or delinking groups. The implication is that until the group is persisted,
159 /// the prims in the database still use the old SceneGroupID. That's a problem if the group
160 /// is deleted, because we delete groups by searching for prims by their SceneGroupID.
161 /// </summary>
162 public bool GroupContainsForeignPrims
163 {
164 private set
165 {
166 m_groupContainsForeignPrims = value;
167 if (m_groupContainsForeignPrims)
168 HasGroupChanged = true;
169 }
170
171 get { return m_groupContainsForeignPrims; }
172 }
173
174
175 private bool isTimeToPersist()
176 {
177 if (IsSelected || IsDeleted || IsAttachment)
178 return false;
179 if (!m_hasGroupChanged)
180 return false;
181 if (m_scene.ShuttingDown)
182 return true;
183 long currentTime = DateTime.Now.Ticks;
184 if (currentTime - timeLastChanged > m_scene.m_dontPersistBefore || currentTime - timeFirstChanged > m_scene.m_persistAfter)
185 return true;
186 return false;
187 }
188
189 /// <summary>
190 /// Is this scene object acting as an attachment?
191 /// </summary>
192 public bool IsAttachment { get; set; }
193
194 /// <summary>
195 /// The avatar to which this scene object is attached.
196 /// </summary>
197 /// <remarks>
198 /// If we're not attached to an avatar then this is UUID.Zero
199 /// </remarks>
200 public UUID AttachedAvatar { get; set; }
201
202 /// <summary>
203 /// Attachment point of this scene object to an avatar.
204 /// </summary>
205 /// <remarks>
206 /// 0 if we're not attached to anything
207 /// </remarks>
208 public uint AttachmentPoint
209 {
210 get
211 {
212 return m_rootPart.Shape.State;
213 }
214
215 set
216 {
217 IsAttachment = value != 0;
218 m_rootPart.Shape.State = (byte)value;
219 }
220 }
221
222 /// <summary>
223 /// If this scene object has an attachment point then indicate whether there is a point where
224 /// attachments are perceivable by avatars other than the avatar to which this object is attached.
225 /// </summary>
226 /// <remarks>
227 /// HUDs are not perceivable by other avatars.
228 /// </remarks>
229 public bool HasPrivateAttachmentPoint
230 {
231 get
232 {
233 return AttachmentPoint >= (uint)OpenMetaverse.AttachmentPoint.HUDCenter2
234 && AttachmentPoint <= (uint)OpenMetaverse.AttachmentPoint.HUDBottomRight;
235 }
236 }
237
238 public void ClearPartAttachmentData()
239 {
240 AttachmentPoint = 0;
241
242 // Even though we don't use child part state parameters for attachments any more, we still need to set
243 // these to zero since having them non-zero in rezzed scene objects will crash some clients. Even if
244 // we store them correctly, scene objects that we receive from elsewhere might not.
245 foreach (SceneObjectPart part in Parts)
246 part.Shape.State = 0;
247 }
248
249 /// <summary>
250 /// Is this scene object phantom?
251 /// </summary>
252 /// <remarks>
253 /// Updating must currently take place through UpdatePrimFlags()
254 /// </remarks>
255 public bool IsPhantom
256 {
257 get { return (RootPart.Flags & PrimFlags.Phantom) != 0; }
258 }
259
260 /// <summary>
261 /// Does this scene object use physics?
262 /// </summary>
263 /// <remarks>
264 /// Updating must currently take place through UpdatePrimFlags()
265 /// </remarks>
266 public bool UsesPhysics
267 {
268 get { return (RootPart.Flags & PrimFlags.Physics) != 0; }
269 }
270
271 /// <summary>
272 /// Is this scene object temporary?
273 /// </summary>
274 /// <remarks>
275 /// Updating must currently take place through UpdatePrimFlags()
276 /// </remarks>
277 public bool IsTemporary
278 {
279 get { return (RootPart.Flags & PrimFlags.TemporaryOnRez) != 0; }
280 }
281
282 public bool IsVolumeDetect
283 {
284 get { return RootPart.VolumeDetectActive; }
285 }
286
287 private Vector3 lastPhysGroupPos;
288 private Quaternion lastPhysGroupRot;
289
290 /// <summary>
291 /// Is this entity set to be saved in persistent storage?
292 /// </summary>
293 public bool Backup { get; private set; }
294
295 protected MapAndArray<UUID, SceneObjectPart> m_parts = new MapAndArray<UUID, SceneObjectPart>();
296
297 protected ulong m_regionHandle;
298 protected SceneObjectPart m_rootPart;
299 // private Dictionary<UUID, scriptEvents> m_scriptEvents = new Dictionary<UUID, scriptEvents>();
300
301 private Dictionary<uint, scriptPosTarget> m_targets = new Dictionary<uint, scriptPosTarget>();
302 private Dictionary<uint, scriptRotTarget> m_rotTargets = new Dictionary<uint, scriptRotTarget>();
303
304 private bool m_scriptListens_atTarget;
305 private bool m_scriptListens_notAtTarget;
306
307 private bool m_scriptListens_atRotTarget;
308 private bool m_scriptListens_notAtRotTarget;
309
310 internal Dictionary<UUID, string> m_savedScriptState;
311
312 #region Properties
313
314 /// <summary>
315 /// The name of an object grouping is always the same as its root part
316 /// </summary>
317 public override string Name
318 {
319 get { return RootPart.Name; }
320 set { RootPart.Name = value; }
321 }
322
323 public string Description
324 {
325 get { return RootPart.Description; }
326 set { RootPart.Description = value; }
327 }
328
329 /// <summary>
330 /// Added because the Parcel code seems to use it
331 /// but not sure a object should have this
332 /// as what does it tell us? that some avatar has selected it (but not what Avatar/user)
333 /// think really there should be a list (or whatever) in each scenepresence
334 /// saying what prim(s) that user has selected.
335 /// </summary>
336 protected bool m_isSelected = false;
337
338 /// <summary>
339 /// Number of prims in this group
340 /// </summary>
341 public int PrimCount
342 {
343 get { return m_parts.Count; }
344 }
345
346 public Quaternion GroupRotation
347 {
348 get { return m_rootPart.RotationOffset; }
349 }
350
351 public Vector3 GroupScale
352 {
353 get
354 {
355 Vector3 minScale = new Vector3(Constants.MaximumRegionSize, Constants.MaximumRegionSize, Constants.MaximumRegionSize);
356 Vector3 maxScale = Vector3.Zero;
357 Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f);
358
359 SceneObjectPart[] parts = m_parts.GetArray();
360 for (int i = 0; i < parts.Length; i++)
361 {
362 SceneObjectPart part = parts[i];
363 Vector3 partscale = part.Scale;
364 Vector3 partoffset = part.OffsetPosition;
365
366 minScale.X = (partscale.X + partoffset.X < minScale.X) ? partscale.X + partoffset.X : minScale.X;
367 minScale.Y = (partscale.Y + partoffset.Y < minScale.Y) ? partscale.Y + partoffset.Y : minScale.Y;
368 minScale.Z = (partscale.Z + partoffset.Z < minScale.Z) ? partscale.Z + partoffset.Z : minScale.Z;
369
370 maxScale.X = (partscale.X + partoffset.X > maxScale.X) ? partscale.X + partoffset.X : maxScale.X;
371 maxScale.Y = (partscale.Y + partoffset.Y > maxScale.Y) ? partscale.Y + partoffset.Y : maxScale.Y;
372 maxScale.Z = (partscale.Z + partoffset.Z > maxScale.Z) ? partscale.Z + partoffset.Z : maxScale.Z;
373 }
374
375 finalScale.X = (minScale.X > maxScale.X) ? minScale.X : maxScale.X;
376 finalScale.Y = (minScale.Y > maxScale.Y) ? minScale.Y : maxScale.Y;
377 finalScale.Z = (minScale.Z > maxScale.Z) ? minScale.Z : maxScale.Z;
378
379 return finalScale;
380 }
381 }
382
383 public UUID GroupID
384 {
385 get { return m_rootPart.GroupID; }
386 set { m_rootPart.GroupID = value; }
387 }
388
389 public SceneObjectPart[] Parts
390 {
391 get { return m_parts.GetArray(); }
392 }
393
394 public bool ContainsPart(UUID partID)
395 {
396 return m_parts.ContainsKey(partID);
397 }
398
399 /// <summary>
400 /// Does this group contain the given part?
401 /// should be able to remove these methods once we have a entity index in scene
402 /// </summary>
403 /// <param name="localID"></param>
404 /// <returns></returns>
405 public bool ContainsPart(uint localID)
406 {
407 SceneObjectPart[] parts = m_parts.GetArray();
408 for (int i = 0; i < parts.Length; i++)
409 {
410 if (parts[i].LocalId == localID)
411 return true;
412 }
413
414 return false;
415 }
416
417 /// <value>
418 /// The root part of this scene object
419 /// </value>
420 public SceneObjectPart RootPart
421 {
422 get { return m_rootPart; }
423 }
424
425 public ulong RegionHandle
426 {
427 get { return m_regionHandle; }
428 set
429 {
430 m_regionHandle = value;
431 SceneObjectPart[] parts = m_parts.GetArray();
432 for (int i = 0; i < parts.Length; i++)
433 parts[i].RegionHandle = value;
434 }
435 }
436
437 /// <summary>
438 /// Check both the attachment property and the relevant properties of the underlying root part.
439 /// </summary>
440 /// <remarks>
441 /// This is necessary in some cases, particularly when a scene object has just crossed into a region and doesn't
442 /// have the IsAttachment property yet checked.
443 ///
444 /// FIXME: However, this should be fixed so that this property
445 /// propertly reflects the underlying status.
446 /// </remarks>
447 /// <returns></returns>
448 public bool IsAttachmentCheckFull()
449 {
450 return (IsAttachment || (m_rootPart.Shape.PCode == (byte)PCodeEnum.Primitive && m_rootPart.Shape.State != 0));
451 }
452
453 private struct avtocrossInfo
454 {
455 public ScenePresence av;
456 public uint ParentID;
457 }
458
459 /// <summary>
460 /// The absolute position of this scene object in the scene
461 /// </summary>
462 public override Vector3 AbsolutePosition
463 {
464 get { return m_rootPart.GroupPosition; }
465 set
466 {
467 Vector3 val = value;
468
469 if (Scene != null)
470 {
471 if (
472 !Scene.PositionIsInCurrentRegion(val)
473 && !IsAttachmentCheckFull()
474 && (!Scene.LoadingPrims)
475 )
476 {
477 IEntityTransferModule entityTransfer = m_scene.RequestModuleInterface<IEntityTransferModule>();
478 string version = String.Empty;
479 Vector3 newpos = Vector3.Zero;
480 string failureReason = String.Empty;
481 OpenSim.Services.Interfaces.GridRegion destination = null;
482
483 if (m_rootPart.KeyframeMotion != null)
484 m_rootPart.KeyframeMotion.StartCrossingCheck();
485
486 bool canCross = true;
487 foreach (ScenePresence av in GetSittingAvatars())
488 {
489 // We need to cross these agents. First, let's find
490 // out if any of them can't cross for some reason.
491 // We have to deny the crossing entirely if any
492 // of them are banned. Alternatively, we could
493 // unsit banned agents....
494
495
496 // We set the avatar position as being the object
497 // position to get the region to send to
498 if ((destination = entityTransfer.GetDestination(m_scene, av.UUID, val, out version, out newpos, out failureReason)) == null)
499 {
500 canCross = false;
501 break;
502 }
503
504 m_log.DebugFormat("[SCENE OBJECT]: Avatar {0} needs to be crossed to {1}", av.Name, destination.RegionName);
505 }
506
507 if (canCross)
508 {
509 // We unparent the SP quietly so that it won't
510 // be made to stand up
511
512 List<avtocrossInfo> avsToCross = new List<avtocrossInfo>();
513
514 foreach (ScenePresence av in GetSittingAvatars())
515 {
516 avtocrossInfo avinfo = new avtocrossInfo();
517 SceneObjectPart parentPart = m_scene.GetSceneObjectPart(av.ParentID);
518 if (parentPart != null)
519 av.ParentUUID = parentPart.UUID;
520
521 avinfo.av = av;
522 avinfo.ParentID = av.ParentID;
523 avsToCross.Add(avinfo);
524
525 av.PrevSitOffset = av.OffsetPosition;
526 av.ParentID = 0;
527 }
528
529 m_scene.CrossPrimGroupIntoNewRegion(val, this, true);
530
531 // Normalize
532 if (val.X >= m_scene.RegionInfo.RegionSizeX)
533 val.X -= m_scene.RegionInfo.RegionSizeX;
534 if (val.Y >= m_scene.RegionInfo.RegionSizeY)
535 val.Y -= m_scene.RegionInfo.RegionSizeY;
536 if (val.X < 0)
537 val.X += m_scene.RegionInfo.RegionSizeX;
538 if (val.Y < 0)
539 val.Y += m_scene.RegionInfo.RegionSizeY;
540
541 // If it's deleted, crossing was successful
542 if (IsDeleted)
543 {
544 foreach (avtocrossInfo avinfo in avsToCross)
545 {
546 ScenePresence av = avinfo.av;
547 if (!av.IsInTransit) // just in case...
548 {
549 m_log.DebugFormat("[SCENE OBJECT]: Crossing avatar {0} to {1}", av.Name, val);
550
551 av.IsInTransit = true;
552
553 // A temporary measure to allow regression tests to work.
554 // Quite possibly, all BeginInvoke() calls should be replaced by Util.FireAndForget
555 // or similar since BeginInvoke() always uses the system threadpool to launch
556 // threads rather than any replace threadpool that we might be using.
557 if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
558 {
559 entityTransfer.CrossAgentToNewRegionAsync(av, val, destination, av.Flying, version);
560 CrossAgentToNewRegionCompleted(av);
561 }
562 else
563 {
564 CrossAgentToNewRegionDelegate d = entityTransfer.CrossAgentToNewRegionAsync;
565 d.BeginInvoke(
566 av, val, destination, av.Flying, version,
567 ar => CrossAgentToNewRegionCompleted(d.EndInvoke(ar)), null);
568 }
569 }
570 else
571 {
572 m_log.DebugFormat("[SCENE OBJECT]: Not crossing avatar {0} to {1} because it's already in transit", av.Name, val);
573 }
574 }
575
576 return;
577 }
578 else // cross failed, put avas back ??
579 {
580 foreach (avtocrossInfo avinfo in avsToCross)
581 {
582 ScenePresence av = avinfo.av;
583 av.ParentUUID = UUID.Zero;
584 av.ParentID = avinfo.ParentID;
585 }
586 }
587 }
588 else
589 {
590 if (m_rootPart.KeyframeMotion != null)
591 m_rootPart.KeyframeMotion.CrossingFailure();
592
593 if (RootPart.PhysActor != null)
594 {
595 RootPart.PhysActor.CrossingFailure();
596 }
597 }
598
599 Vector3 oldp = AbsolutePosition;
600 val.X = Util.Clamp<float>(oldp.X, 0.5f, (float)m_scene.RegionInfo.RegionSizeX - 0.5f);
601 val.Y = Util.Clamp<float>(oldp.Y, 0.5f, (float)m_scene.RegionInfo.RegionSizeY - 0.5f);
602 val.Z = Util.Clamp<float>(oldp.Z, 0.5f, Constants.RegionHeight);
603 }
604 }
605
606 if (RootPart.GetStatusSandbox())
607 {
608 if (Util.GetDistanceTo(RootPart.StatusSandboxPos, value) > 10)
609 {
610 RootPart.ScriptSetPhysicsStatus(false);
611
612 if (Scene != null)
613 Scene.SimChat(Utils.StringToBytes("Hit Sandbox Limit"),
614 ChatTypeEnum.DebugChannel, 0x7FFFFFFF, RootPart.AbsolutePosition, Name, UUID, false);
615
616 return;
617 }
618 }
619
620 // Restuff the new GroupPosition into each SOP of the linkset.
621 // This has the affect of resetting and tainting the physics actors.
622 SceneObjectPart[] parts = m_parts.GetArray();
623 for (int i = 0; i < parts.Length; i++)
624 parts[i].GroupPosition = val;
625
626 //if (m_rootPart.PhysActor != null)
627 //{
628 //m_rootPart.PhysActor.Position =
629 //new PhysicsVector(m_rootPart.GroupPosition.X, m_rootPart.GroupPosition.Y,
630 //m_rootPart.GroupPosition.Z);
631 //m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
632 //}
633
634 if (Scene != null)
635 Scene.EventManager.TriggerParcelPrimCountTainted();
636 }
637 }
638
639 public override Vector3 Velocity
640 {
641 get { return RootPart.Velocity; }
642 set { RootPart.Velocity = value; }
643 }
644
645 private void CrossAgentToNewRegionCompleted(ScenePresence agent)
646 {
647 //// If the cross was successful, this agent is a child agent
648 if (agent.IsChildAgent)
649 {
650 if (agent.ParentUUID != UUID.Zero)
651 {
652 agent.ParentPart = null;
653// agent.ParentPosition = Vector3.Zero;
654// agent.ParentUUID = UUID.Zero;
655 }
656 }
657
658 agent.ParentUUID = UUID.Zero;
659// agent.Reset();
660// else // Not successful
661// agent.RestoreInCurrentScene();
662
663 // In any case
664 agent.IsInTransit = false;
665
666 m_log.DebugFormat("[SCENE OBJECT]: Crossing agent {0} {1} completed.", agent.Firstname, agent.Lastname);
667 }
668
669 public override uint LocalId
670 {
671 get { return m_rootPart.LocalId; }
672 set { m_rootPart.LocalId = value; }
673 }
674
675 public override UUID UUID
676 {
677 get { return m_rootPart.UUID; }
678 set
679 {
680 lock (m_parts.SyncRoot)
681 {
682 m_parts.Remove(m_rootPart.UUID);
683 m_rootPart.UUID = value;
684 m_parts.Add(value, m_rootPart);
685 }
686 }
687 }
688
689 public UUID LastOwnerID
690 {
691 get { return m_rootPart.LastOwnerID; }
692 set { m_rootPart.LastOwnerID = value; }
693 }
694
695 public UUID OwnerID
696 {
697 get { return m_rootPart.OwnerID; }
698 set { m_rootPart.OwnerID = value; }
699 }
700
701 public float Damage
702 {
703 get { return m_rootPart.Damage; }
704 set { m_rootPart.Damage = value; }
705 }
706
707 public Color Color
708 {
709 get { return m_rootPart.Color; }
710 set { m_rootPart.Color = value; }
711 }
712
713 public string Text
714 {
715 get {
716 string returnstr = m_rootPart.Text;
717 if (returnstr.Length > 255)
718 {
719 returnstr = returnstr.Substring(0, 255);
720 }
721 return returnstr;
722 }
723 set { m_rootPart.Text = value; }
724 }
725
726 /// <summary>
727 /// If set to true then the scene object can be backed up in principle, though this will only actually occur
728 /// if Backup is set. If false then the scene object will never be backed up, Backup will always be false.
729 /// </summary>
730 protected virtual bool CanBeBackedUp
731 {
732 get { return true; }
733 }
734
735 public bool IsSelected
736 {
737 get { return m_isSelected; }
738 set
739 {
740 m_isSelected = value;
741 // Tell physics engine that group is selected
742
743 PhysicsActor pa = m_rootPart.PhysActor;
744 if (pa != null)
745 {
746 pa.Selected = value;
747
748 // Pass it on to the children.
749 SceneObjectPart[] parts = m_parts.GetArray();
750 for (int i = 0; i < parts.Length; i++)
751 {
752 SceneObjectPart child = parts[i];
753
754 PhysicsActor childPa = child.PhysActor;
755 if (childPa != null)
756 childPa.Selected = value;
757 }
758 }
759 if (RootPart.KeyframeMotion != null)
760 RootPart.KeyframeMotion.Selected = value;
761 }
762 }
763
764 private SceneObjectPart m_PlaySoundMasterPrim = null;
765 public SceneObjectPart PlaySoundMasterPrim
766 {
767 get { return m_PlaySoundMasterPrim; }
768 set { m_PlaySoundMasterPrim = value; }
769 }
770
771 private List<SceneObjectPart> m_PlaySoundSlavePrims = new List<SceneObjectPart>();
772 public List<SceneObjectPart> PlaySoundSlavePrims
773 {
774 get { return m_PlaySoundSlavePrims; }
775 set { m_PlaySoundSlavePrims = value; }
776 }
777
778 private SceneObjectPart m_LoopSoundMasterPrim = null;
779 public SceneObjectPart LoopSoundMasterPrim
780 {
781 get { return m_LoopSoundMasterPrim; }
782 set { m_LoopSoundMasterPrim = value; }
783 }
784
785 private List<SceneObjectPart> m_LoopSoundSlavePrims = new List<SceneObjectPart>();
786 public List<SceneObjectPart> LoopSoundSlavePrims
787 {
788 get { return m_LoopSoundSlavePrims; }
789 set { m_LoopSoundSlavePrims = value; }
790 }
791
792 /// <summary>
793 /// The UUID for the region this object is in.
794 /// </summary>
795 public UUID RegionUUID
796 {
797 get
798 {
799 if (m_scene != null)
800 {
801 return m_scene.RegionInfo.RegionID;
802 }
803 return UUID.Zero;
804 }
805 }
806
807 /// <summary>
808 /// The item ID that this object was rezzed from, if applicable.
809 /// </summary>
810 /// <remarks>
811 /// If not applicable will be UUID.Zero
812 /// </remarks>
813 public UUID FromItemID { get; set; }
814
815 /// <summary>
816 /// Refers to the SceneObjectPart.UUID property of the object that this object was rezzed from, if applicable.
817 /// </summary>
818 /// <remarks>
819 /// If not applicable will be UUID.Zero
820 /// </remarks>
821 public UUID FromPartID { get; set; }
822
823 /// <summary>
824 /// The folder ID that this object was rezzed from, if applicable.
825 /// </summary>
826 /// <remarks>
827 /// If not applicable will be UUID.Zero
828 /// </remarks>
829 public UUID FromFolderID { get; set; }
830
831 /// <summary>
832 /// If true then grabs are blocked no matter what the individual part BlockGrab setting.
833 /// </summary>
834 /// <value><c>true</c> if block grab override; otherwise, <c>false</c>.</value>
835 public bool BlockGrabOverride { get; set; }
836
837 /// <summary>
838 /// IDs of all avatars sat on this scene object.
839 /// </summary>
840 /// <remarks>
841 /// We need this so that we can maintain a linkset wide ordering of avatars sat on different parts.
842 /// This must be locked before it is read or written.
843 /// SceneObjectPart sitting avatar add/remove code also locks on this object to avoid race conditions.
844 /// No avatar should appear more than once in this list.
845 /// Do not manipulate this list directly - use the Add/Remove sitting avatar methods on SceneObjectPart.
846 /// </remarks>
847 protected internal List<ScenePresence> m_sittingAvatars = new List<ScenePresence>();
848
849 #endregion
850
851// ~SceneObjectGroup()
852// {
853// //m_log.DebugFormat("[SCENE OBJECT GROUP]: Destructor called for {0}, local id {1}", Name, LocalId);
854// Console.WriteLine("Destructor called for {0}, local id {1}", Name, LocalId);
855// }
856
857 #region Constructors
858
859 /// <summary>
860 /// Constructor
861 /// </summary>
862 public SceneObjectGroup()
863 {
864 }
865
866 /// <summary>
867 /// This constructor creates a SceneObjectGroup using a pre-existing SceneObjectPart.
868 /// The original SceneObjectPart will be used rather than a copy, preserving
869 /// its existing localID and UUID.
870 /// </summary>
871 /// <param name='part'>Root part for this scene object.</param>
872 public SceneObjectGroup(SceneObjectPart part) : this()
873 {
874 SetRootPart(part);
875 }
876
877 /// <summary>
878 /// Constructor. This object is added to the scene later via AttachToScene()
879 /// </summary>
880 public SceneObjectGroup(UUID ownerID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape)
881 :this(new SceneObjectPart(ownerID, shape, pos, rot, Vector3.Zero))
882 {
883 }
884
885 /// <summary>
886 /// Constructor.
887 /// </summary>
888 public SceneObjectGroup(UUID ownerID, Vector3 pos, PrimitiveBaseShape shape)
889 : this(ownerID, pos, Quaternion.Identity, shape)
890 {
891 }
892
893 public void LoadScriptState(XmlDocument doc)
894 {
895 XmlNodeList nodes = doc.GetElementsByTagName("SavedScriptState");
896 if (nodes.Count > 0)
897 {
898 if (m_savedScriptState == null)
899 m_savedScriptState = new Dictionary<UUID, string>();
900 foreach (XmlNode node in nodes)
901 {
902 if (node.Attributes["UUID"] != null)
903 {
904 UUID itemid = new UUID(node.Attributes["UUID"].Value);
905 if (itemid != UUID.Zero)
906 m_savedScriptState[itemid] = node.InnerXml;
907 }
908 }
909 }
910 }
911
912 public void LoadScriptState(XmlReader reader)
913 {
914// m_log.DebugFormat("[SCENE OBJECT GROUP]: Looking for script state for {0}", Name);
915
916 while (true)
917 {
918 if (reader.Name == "SavedScriptState" && reader.NodeType == XmlNodeType.Element)
919 {
920// m_log.DebugFormat("[SCENE OBJECT GROUP]: Loading script state for {0}", Name);
921
922 if (m_savedScriptState == null)
923 m_savedScriptState = new Dictionary<UUID, string>();
924
925 string uuid = reader.GetAttribute("UUID");
926
927 // Even if there is no UUID attribute for some strange reason, we must always read the inner XML
928 // so we don't continually keep checking the same SavedScriptedState element.
929 string innerXml = reader.ReadInnerXml();
930
931 if (uuid != null)
932 {
933// m_log.DebugFormat("[SCENE OBJECT GROUP]: Found state for item ID {0} in object {1}", uuid, Name);
934
935 UUID itemid = new UUID(uuid);
936 if (itemid != UUID.Zero)
937 m_savedScriptState[itemid] = innerXml;
938 }
939 else
940 {
941 m_log.WarnFormat("[SCENE OBJECT GROUP]: SavedScriptState element had no UUID in object {0}", Name);
942 }
943 }
944 else
945 {
946 if (!reader.Read())
947 break;
948 }
949 }
950 }
951
952 /// <summary>
953 /// Hooks this object up to the backup event so that it is persisted to the database when the update thread executes.
954 /// </summary>
955 public virtual void AttachToBackup()
956 {
957 if (CanBeBackedUp)
958 {
959// m_log.DebugFormat(
960// "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);
961
962 if (!Backup)
963 m_scene.EventManager.OnBackup += ProcessBackup;
964
965 Backup = true;
966 }
967 }
968
969 /// <summary>
970 /// Attach this object to a scene. It will also now appear to agents.
971 /// </summary>
972 /// <param name="scene"></param>
973 public void AttachToScene(Scene scene)
974 {
975 m_scene = scene;
976 RegionHandle = m_scene.RegionInfo.RegionHandle;
977
978 if (m_rootPart.Shape.PCode != 9 || m_rootPart.Shape.State == 0)
979 m_rootPart.ParentID = 0;
980 if (m_rootPart.LocalId == 0)
981 m_rootPart.LocalId = m_scene.AllocateLocalId();
982
983 SceneObjectPart[] parts = m_parts.GetArray();
984 for (int i = 0; i < parts.Length; i++)
985 {
986 SceneObjectPart part = parts[i];
987 if (part.KeyframeMotion != null)
988 {
989 part.KeyframeMotion.UpdateSceneObject(this);
990 }
991
992 if (Object.ReferenceEquals(part, m_rootPart))
993 continue;
994
995 if (part.LocalId == 0)
996 part.LocalId = m_scene.AllocateLocalId();
997
998 part.ParentID = m_rootPart.LocalId;
999 //m_log.DebugFormat("[SCENE]: Given local id {0} to part {1}, linknum {2}, parent {3} {4}", part.LocalId, part.UUID, part.LinkNum, part.ParentID, part.ParentUUID);
1000 }
1001
1002 ApplyPhysics();
1003
1004 // Don't trigger the update here - otherwise some client issues occur when multiple updates are scheduled
1005 // for the same object with very different properties. The caller must schedule the update.
1006 //ScheduleGroupForFullUpdate();
1007 }
1008
1009 public EntityIntersection TestIntersection(Ray hRay, bool frontFacesOnly, bool faceCenters)
1010 {
1011 // We got a request from the inner_scene to raytrace along the Ray hRay
1012 // We're going to check all of the prim in this group for intersection with the ray
1013 // If we get a result, we're going to find the closest result to the origin of the ray
1014 // and send back the intersection information back to the innerscene.
1015
1016 EntityIntersection result = new EntityIntersection();
1017
1018 SceneObjectPart[] parts = m_parts.GetArray();
1019 for (int i = 0; i < parts.Length; i++)
1020 {
1021 SceneObjectPart part = parts[i];
1022
1023 // Temporary commented to stop compiler warning
1024 //Vector3 partPosition =
1025 // new Vector3(part.AbsolutePosition.X, part.AbsolutePosition.Y, part.AbsolutePosition.Z);
1026 Quaternion parentrotation = GroupRotation;
1027
1028 // Telling the prim to raytrace.
1029 //EntityIntersection inter = part.TestIntersection(hRay, parentrotation);
1030
1031 EntityIntersection inter = part.TestIntersectionOBB(hRay, parentrotation, frontFacesOnly, faceCenters);
1032
1033 // This may need to be updated to the maximum draw distance possible..
1034 // We might (and probably will) be checking for prim creation from other sims
1035 // when the camera crosses the border.
1036 float idist = Constants.RegionSize;
1037
1038 if (inter.HitTF)
1039 {
1040 // We need to find the closest prim to return to the testcaller along the ray
1041 if (inter.distance < idist)
1042 {
1043 result.HitTF = true;
1044 result.ipoint = inter.ipoint;
1045 result.obj = part;
1046 result.normal = inter.normal;
1047 result.distance = inter.distance;
1048 }
1049 }
1050 }
1051
1052 return result;
1053 }
1054
1055 /// <summary>
1056 /// Gets a vector representing the size of the bounding box containing all the prims in the group
1057 /// Treats all prims as rectangular, so no shape (cut etc) is taken into account
1058 /// offsetHeight is the offset in the Z axis from the centre of the bounding box to the centre of the root prim
1059 /// </summary>
1060 /// <returns></returns>
1061 public void GetAxisAlignedBoundingBoxRaw(out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ)
1062 {
1063 maxX = -256f;
1064 maxY = -256f;
1065 maxZ = -256f;
1066 minX = 10000f;
1067 minY = 10000f;
1068 minZ = 10000f;
1069
1070 SceneObjectPart[] parts = m_parts.GetArray();
1071 for (int i = 0; i < parts.Length; i++)
1072 {
1073 SceneObjectPart part = parts[i];
1074
1075 Vector3 worldPos = part.GetWorldPosition();
1076 Vector3 offset = worldPos - AbsolutePosition;
1077 Quaternion worldRot;
1078 if (part.ParentID == 0)
1079 worldRot = part.RotationOffset;
1080 else
1081 worldRot = part.GetWorldRotation();
1082
1083 Vector3 frontTopLeft;
1084 Vector3 frontTopRight;
1085 Vector3 frontBottomLeft;
1086 Vector3 frontBottomRight;
1087
1088 Vector3 backTopLeft;
1089 Vector3 backTopRight;
1090 Vector3 backBottomLeft;
1091 Vector3 backBottomRight;
1092
1093 Vector3 orig = Vector3.Zero;
1094
1095 frontTopLeft.X = orig.X - (part.Scale.X / 2);
1096 frontTopLeft.Y = orig.Y - (part.Scale.Y / 2);
1097 frontTopLeft.Z = orig.Z + (part.Scale.Z / 2);
1098
1099 frontTopRight.X = orig.X - (part.Scale.X / 2);
1100 frontTopRight.Y = orig.Y + (part.Scale.Y / 2);
1101 frontTopRight.Z = orig.Z + (part.Scale.Z / 2);
1102
1103 frontBottomLeft.X = orig.X - (part.Scale.X / 2);
1104 frontBottomLeft.Y = orig.Y - (part.Scale.Y / 2);
1105 frontBottomLeft.Z = orig.Z - (part.Scale.Z / 2);
1106
1107 frontBottomRight.X = orig.X - (part.Scale.X / 2);
1108 frontBottomRight.Y = orig.Y + (part.Scale.Y / 2);
1109 frontBottomRight.Z = orig.Z - (part.Scale.Z / 2);
1110
1111 backTopLeft.X = orig.X + (part.Scale.X / 2);
1112 backTopLeft.Y = orig.Y - (part.Scale.Y / 2);
1113 backTopLeft.Z = orig.Z + (part.Scale.Z / 2);
1114
1115 backTopRight.X = orig.X + (part.Scale.X / 2);
1116 backTopRight.Y = orig.Y + (part.Scale.Y / 2);
1117 backTopRight.Z = orig.Z + (part.Scale.Z / 2);
1118
1119 backBottomLeft.X = orig.X + (part.Scale.X / 2);
1120 backBottomLeft.Y = orig.Y - (part.Scale.Y / 2);
1121 backBottomLeft.Z = orig.Z - (part.Scale.Z / 2);
1122
1123 backBottomRight.X = orig.X + (part.Scale.X / 2);
1124 backBottomRight.Y = orig.Y + (part.Scale.Y / 2);
1125 backBottomRight.Z = orig.Z - (part.Scale.Z / 2);
1126
1127 frontTopLeft = frontTopLeft * worldRot;
1128 frontTopRight = frontTopRight * worldRot;
1129 frontBottomLeft = frontBottomLeft * worldRot;
1130 frontBottomRight = frontBottomRight * worldRot;
1131
1132 backBottomLeft = backBottomLeft * worldRot;
1133 backBottomRight = backBottomRight * worldRot;
1134 backTopLeft = backTopLeft * worldRot;
1135 backTopRight = backTopRight * worldRot;
1136
1137
1138 frontTopLeft += offset;
1139 frontTopRight += offset;
1140 frontBottomLeft += offset;
1141 frontBottomRight += offset;
1142
1143 backBottomLeft += offset;
1144 backBottomRight += offset;
1145 backTopLeft += offset;
1146 backTopRight += offset;
1147
1148 if (frontTopRight.X > maxX)
1149 maxX = frontTopRight.X;
1150 if (frontTopLeft.X > maxX)
1151 maxX = frontTopLeft.X;
1152 if (frontBottomRight.X > maxX)
1153 maxX = frontBottomRight.X;
1154 if (frontBottomLeft.X > maxX)
1155 maxX = frontBottomLeft.X;
1156
1157 if (backTopRight.X > maxX)
1158 maxX = backTopRight.X;
1159 if (backTopLeft.X > maxX)
1160 maxX = backTopLeft.X;
1161 if (backBottomRight.X > maxX)
1162 maxX = backBottomRight.X;
1163 if (backBottomLeft.X > maxX)
1164 maxX = backBottomLeft.X;
1165
1166 if (frontTopRight.X < minX)
1167 minX = frontTopRight.X;
1168 if (frontTopLeft.X < minX)
1169 minX = frontTopLeft.X;
1170 if (frontBottomRight.X < minX)
1171 minX = frontBottomRight.X;
1172 if (frontBottomLeft.X < minX)
1173 minX = frontBottomLeft.X;
1174
1175 if (backTopRight.X < minX)
1176 minX = backTopRight.X;
1177 if (backTopLeft.X < minX)
1178 minX = backTopLeft.X;
1179 if (backBottomRight.X < minX)
1180 minX = backBottomRight.X;
1181 if (backBottomLeft.X < minX)
1182 minX = backBottomLeft.X;
1183
1184 //
1185 if (frontTopRight.Y > maxY)
1186 maxY = frontTopRight.Y;
1187 if (frontTopLeft.Y > maxY)
1188 maxY = frontTopLeft.Y;
1189 if (frontBottomRight.Y > maxY)
1190 maxY = frontBottomRight.Y;
1191 if (frontBottomLeft.Y > maxY)
1192 maxY = frontBottomLeft.Y;
1193
1194 if (backTopRight.Y > maxY)
1195 maxY = backTopRight.Y;
1196 if (backTopLeft.Y > maxY)
1197 maxY = backTopLeft.Y;
1198 if (backBottomRight.Y > maxY)
1199 maxY = backBottomRight.Y;
1200 if (backBottomLeft.Y > maxY)
1201 maxY = backBottomLeft.Y;
1202
1203 if (frontTopRight.Y < minY)
1204 minY = frontTopRight.Y;
1205 if (frontTopLeft.Y < minY)
1206 minY = frontTopLeft.Y;
1207 if (frontBottomRight.Y < minY)
1208 minY = frontBottomRight.Y;
1209 if (frontBottomLeft.Y < minY)
1210 minY = frontBottomLeft.Y;
1211
1212 if (backTopRight.Y < minY)
1213 minY = backTopRight.Y;
1214 if (backTopLeft.Y < minY)
1215 minY = backTopLeft.Y;
1216 if (backBottomRight.Y < minY)
1217 minY = backBottomRight.Y;
1218 if (backBottomLeft.Y < minY)
1219 minY = backBottomLeft.Y;
1220
1221 //
1222 if (frontTopRight.Z > maxZ)
1223 maxZ = frontTopRight.Z;
1224 if (frontTopLeft.Z > maxZ)
1225 maxZ = frontTopLeft.Z;
1226 if (frontBottomRight.Z > maxZ)
1227 maxZ = frontBottomRight.Z;
1228 if (frontBottomLeft.Z > maxZ)
1229 maxZ = frontBottomLeft.Z;
1230
1231 if (backTopRight.Z > maxZ)
1232 maxZ = backTopRight.Z;
1233 if (backTopLeft.Z > maxZ)
1234 maxZ = backTopLeft.Z;
1235 if (backBottomRight.Z > maxZ)
1236 maxZ = backBottomRight.Z;
1237 if (backBottomLeft.Z > maxZ)
1238 maxZ = backBottomLeft.Z;
1239
1240 if (frontTopRight.Z < minZ)
1241 minZ = frontTopRight.Z;
1242 if (frontTopLeft.Z < minZ)
1243 minZ = frontTopLeft.Z;
1244 if (frontBottomRight.Z < minZ)
1245 minZ = frontBottomRight.Z;
1246 if (frontBottomLeft.Z < minZ)
1247 minZ = frontBottomLeft.Z;
1248
1249 if (backTopRight.Z < minZ)
1250 minZ = backTopRight.Z;
1251 if (backTopLeft.Z < minZ)
1252 minZ = backTopLeft.Z;
1253 if (backBottomRight.Z < minZ)
1254 minZ = backBottomRight.Z;
1255 if (backBottomLeft.Z < minZ)
1256 minZ = backBottomLeft.Z;
1257 }
1258 }
1259
1260 public Vector3 GetAxisAlignedBoundingBox(out float offsetHeight)
1261 {
1262 float minX;
1263 float maxX;
1264 float minY;
1265 float maxY;
1266 float minZ;
1267 float maxZ;
1268
1269 GetAxisAlignedBoundingBoxRaw(out minX, out maxX, out minY, out maxY, out minZ, out maxZ);
1270 Vector3 boundingBox = new Vector3(maxX - minX, maxY - minY, maxZ - minZ);
1271
1272 offsetHeight = 0;
1273 float lower = (minZ * -1);
1274 if (lower > maxZ)
1275 {
1276 offsetHeight = lower - (boundingBox.Z / 2);
1277
1278 }
1279 else if (maxZ > lower)
1280 {
1281 offsetHeight = maxZ - (boundingBox.Z / 2);
1282 offsetHeight *= -1;
1283 }
1284
1285 // m_log.InfoFormat("BoundingBox is {0} , {1} , {2} ", boundingBox.X, boundingBox.Y, boundingBox.Z);
1286 return boundingBox;
1287 }
1288
1289 #endregion
1290
1291 public void SaveScriptedState(XmlTextWriter writer)
1292 {
1293 XmlDocument doc = new XmlDocument();
1294 Dictionary<UUID,string> states = new Dictionary<UUID,string>();
1295
1296 SceneObjectPart[] parts = m_parts.GetArray();
1297 for (int i = 0; i < parts.Length; i++)
1298 {
1299 Dictionary<UUID, string> pstates = parts[i].Inventory.GetScriptStates();
1300 foreach (KeyValuePair<UUID, string> kvp in pstates)
1301 states.Add(kvp.Key, kvp.Value);
1302 }
1303
1304 if (states.Count > 0)
1305 {
1306 // Now generate the necessary XML wrappings
1307 writer.WriteStartElement(String.Empty, "GroupScriptStates", String.Empty);
1308 foreach (UUID itemid in states.Keys)
1309 {
1310 doc.LoadXml(states[itemid]);
1311 writer.WriteStartElement(String.Empty, "SavedScriptState", String.Empty);
1312 writer.WriteAttributeString(String.Empty, "UUID", String.Empty, itemid.ToString());
1313 writer.WriteRaw(doc.DocumentElement.OuterXml); // Writes ScriptState element
1314 writer.WriteEndElement(); // End of SavedScriptState
1315 }
1316 writer.WriteEndElement(); // End of GroupScriptStates
1317 }
1318 }
1319
1320
1321 /// <summary>
1322 ///
1323 /// </summary>
1324 /// <param name="part"></param>
1325 private void SetPartAsNonRoot(SceneObjectPart part)
1326 {
1327 part.ParentID = m_rootPart.LocalId;
1328 part.ClearUndoState();
1329 }
1330
1331 public ushort GetTimeDilation()
1332 {
1333 return Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f);
1334 }
1335
1336 /// <summary>
1337 /// Set a part to act as the root part for this scene object
1338 /// </summary>
1339 /// <param name="part"></param>
1340 public void SetRootPart(SceneObjectPart part)
1341 {
1342 if (part == null)
1343 throw new ArgumentNullException("Cannot give SceneObjectGroup a null root SceneObjectPart");
1344
1345 part.SetParent(this);
1346 m_rootPart = part;
1347 if (!IsAttachment)
1348 part.ParentID = 0;
1349 part.LinkNum = 0;
1350
1351 m_parts.Add(m_rootPart.UUID, m_rootPart);
1352 }
1353
1354 /// <summary>
1355 /// Add a new part to this scene object. The part must already be correctly configured.
1356 /// </summary>
1357 /// <param name="part"></param>
1358 public void AddPart(SceneObjectPart part)
1359 {
1360 part.SetParent(this);
1361 part.LinkNum = m_parts.Add(part.UUID, part);
1362 if (part.LinkNum == 2)
1363 RootPart.LinkNum = 1;
1364 }
1365
1366 /// <summary>
1367 /// Make sure that every non root part has the proper parent root part local id
1368 /// </summary>
1369 private void UpdateParentIDs()
1370 {
1371 SceneObjectPart[] parts = m_parts.GetArray();
1372 for (int i = 0; i < parts.Length; i++)
1373 {
1374 SceneObjectPart part = parts[i];
1375 if (part.UUID != m_rootPart.UUID)
1376 part.ParentID = m_rootPart.LocalId;
1377 }
1378 }
1379
1380 public void RegenerateFullIDs()
1381 {
1382 SceneObjectPart[] parts = m_parts.GetArray();
1383 for (int i = 0; i < parts.Length; i++)
1384 parts[i].UUID = UUID.Random();
1385 }
1386
1387 // justincc: I don't believe this hack is needed any longer, especially since the physics
1388 // parts of set AbsolutePosition were already commented out. By changing HasGroupChanged to false
1389 // this method was preventing proper reload of scene objects.
1390
1391 // dahlia: I had to uncomment it, without it meshing was failing on some prims and objects
1392 // at region startup
1393
1394 // teravus: After this was removed from the linking algorithm, Linked prims no longer collided
1395 // properly when non-physical if they havn't been moved. This breaks ALL builds.
1396 // see: http://opensimulator.org/mantis/view.php?id=3108
1397
1398 // Here's the deal, this is ABSOLUTELY CRITICAL so the physics scene gets the update about the
1399 // position of linkset prims. IF YOU CHANGE THIS, YOU MUST TEST colliding with just linked and
1400 // unmoved prims! As soon as you move a Prim/group, it will collide properly because Absolute
1401 // Position has been set!
1402
1403 public void ResetChildPrimPhysicsPositions()
1404 {
1405 // Setting this SOG's absolute position also loops through and sets the positions
1406 // of the SOP's in this SOG's linkset. This has the side affect of making sure
1407 // the physics world matches the simulated world.
1408 AbsolutePosition = AbsolutePosition; // could someone in the know please explain how this works?
1409
1410 // teravus: AbsolutePosition is NOT a normal property!
1411 // the code in the getter of AbsolutePosition is significantly different then the code in the setter!
1412 // jhurliman: Then why is it a property instead of two methods?
1413 }
1414
1415 public UUID GetPartsFullID(uint localID)
1416 {
1417 SceneObjectPart part = GetPart(localID);
1418 if (part != null)
1419 {
1420 return part.UUID;
1421 }
1422 return UUID.Zero;
1423 }
1424
1425 public void ObjectGrabHandler(uint localId, Vector3 offsetPos, IClientAPI remoteClient)
1426 {
1427 if (m_rootPart.LocalId == localId)
1428 {
1429 OnGrabGroup(offsetPos, remoteClient);
1430 }
1431 else
1432 {
1433 SceneObjectPart part = GetPart(localId);
1434 OnGrabPart(part, offsetPos, remoteClient);
1435 }
1436 }
1437
1438 public virtual void OnGrabPart(SceneObjectPart part, Vector3 offsetPos, IClientAPI remoteClient)
1439 {
1440// m_log.DebugFormat(
1441// "[SCENE OBJECT GROUP]: Processing OnGrabPart for {0} on {1} {2}, offsetPos {3}",
1442// remoteClient.Name, part.Name, part.LocalId, offsetPos);
1443
1444 part.StoreUndoState();
1445 part.OnGrab(offsetPos, remoteClient);
1446 }
1447
1448 public virtual void OnGrabGroup(Vector3 offsetPos, IClientAPI remoteClient)
1449 {
1450 m_scene.EventManager.TriggerGroupGrab(UUID, offsetPos, remoteClient.AgentId);
1451 }
1452
1453 /// <summary>
1454 /// Delete this group from its scene.
1455 /// </summary>
1456 /// <remarks>
1457 /// This only handles the in-world consequences of deletion (e.g. any avatars sitting on it are forcibly stood
1458 /// up and all avatars receive notification of its removal. Removal of the scene object from database backup
1459 /// must be handled by the caller.
1460 /// </remarks>
1461 /// <param name="silent">If true then deletion is not broadcast to clients</param>
1462 public void DeleteGroupFromScene(bool silent)
1463 {
1464 SceneObjectPart[] parts = m_parts.GetArray();
1465 for (int i = 0; i < parts.Length; i++)
1466 {
1467 SceneObjectPart part = parts[i];
1468
1469 Scene.ForEachScenePresence(sp =>
1470 {
1471 if (!sp.IsChildAgent && sp.ParentID == part.LocalId)
1472 sp.StandUp();
1473
1474 if (!silent)
1475 {
1476 part.ClearUpdateSchedule();
1477 if (part == m_rootPart)
1478 {
1479 if (!IsAttachment
1480 || AttachedAvatar == sp.UUID
1481 || !HasPrivateAttachmentPoint)
1482 sp.ControllingClient.SendKillObject(new List<uint> { part.LocalId });
1483 }
1484 }
1485 });
1486 }
1487 }
1488
1489 public void AddScriptLPS(int count)
1490 {
1491 m_scene.SceneGraph.AddToScriptLPS(count);
1492 }
1493
1494 public void AddActiveScriptCount(int count)
1495 {
1496 SceneGraph d = m_scene.SceneGraph;
1497 d.AddActiveScripts(count);
1498 }
1499
1500 public void aggregateScriptEvents()
1501 {
1502 PrimFlags objectflagupdate = (PrimFlags)RootPart.GetEffectiveObjectFlags();
1503
1504 scriptEvents aggregateScriptEvents = 0;
1505
1506 SceneObjectPart[] parts = m_parts.GetArray();
1507 for (int i = 0; i < parts.Length; i++)
1508 {
1509 SceneObjectPart part = parts[i];
1510 if (part == null)
1511 continue;
1512 if (part != RootPart)
1513 part.Flags = objectflagupdate;
1514 aggregateScriptEvents |= part.AggregateScriptEvents;
1515 }
1516
1517 m_scriptListens_atTarget = ((aggregateScriptEvents & scriptEvents.at_target) != 0);
1518 m_scriptListens_notAtTarget = ((aggregateScriptEvents & scriptEvents.not_at_target) != 0);
1519
1520 if (!m_scriptListens_atTarget && !m_scriptListens_notAtTarget)
1521 {
1522 lock (m_targets)
1523 m_targets.Clear();
1524 m_scene.RemoveGroupTarget(this);
1525 }
1526 m_scriptListens_atRotTarget = ((aggregateScriptEvents & scriptEvents.at_rot_target) != 0);
1527 m_scriptListens_notAtRotTarget = ((aggregateScriptEvents & scriptEvents.not_at_rot_target) != 0);
1528
1529 if (!m_scriptListens_atRotTarget && !m_scriptListens_notAtRotTarget)
1530 {
1531 lock (m_rotTargets)
1532 m_rotTargets.Clear();
1533 m_scene.RemoveGroupTarget(this);
1534 }
1535
1536 ScheduleGroupForFullUpdate();
1537 }
1538
1539 public void SetText(string text, Vector3 color, double alpha)
1540 {
1541 Color = Color.FromArgb(0xff - (int) (alpha * 0xff),
1542 (int) (color.X * 0xff),
1543 (int) (color.Y * 0xff),
1544 (int) (color.Z * 0xff));
1545 Text = text;
1546
1547 HasGroupChanged = true;
1548 m_rootPart.ScheduleFullUpdate();
1549 }
1550
1551 /// <summary>
1552 /// Apply physics to this group
1553 /// </summary>
1554 public void ApplyPhysics()
1555 {
1556 // Apply physics to the root prim
1557 m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive);
1558
1559 // Apply physics to child prims
1560 SceneObjectPart[] parts = m_parts.GetArray();
1561 if (parts.Length > 1)
1562 {
1563 for (int i = 0; i < parts.Length; i++)
1564 {
1565 SceneObjectPart part = parts[i];
1566 if (part.LocalId != m_rootPart.LocalId)
1567 part.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), part.VolumeDetectActive);
1568 }
1569
1570 // Hack to get the physics scene geometries in the right spot
1571 ResetChildPrimPhysicsPositions();
1572 }
1573 }
1574
1575 public void SetOwnerId(UUID userId)
1576 {
1577 ForEachPart(delegate(SceneObjectPart part) { part.OwnerID = userId; });
1578 }
1579
1580 public void ForEachPart(Action<SceneObjectPart> whatToDo)
1581 {
1582 SceneObjectPart[] parts = m_parts.GetArray();
1583 for (int i = 0; i < parts.Length; i++)
1584 whatToDo(parts[i]);
1585 }
1586
1587 #region Events
1588
1589 /// <summary>
1590 /// Processes backup.
1591 /// </summary>
1592 /// <param name="datastore"></param>
1593 public virtual void ProcessBackup(ISimulationDataService datastore, bool forcedBackup)
1594 {
1595 if (!Backup)
1596 {
1597// m_log.DebugFormat(
1598// "[WATER WARS]: Ignoring backup of {0} {1} since object is not marked to be backed up", Name, UUID);
1599 return;
1600 }
1601
1602 if (IsDeleted || UUID == UUID.Zero)
1603 {
1604// m_log.DebugFormat(
1605// "[WATER WARS]: Ignoring backup of {0} {1} since object is marked as already deleted", Name, UUID);
1606 return;
1607 }
1608
1609 // Since this is the top of the section of call stack for backing up a particular scene object, don't let
1610 // any exception propogate upwards.
1611 try
1612 {
1613 if (!m_scene.ShuttingDown) // if shutting down then there will be nothing to handle the return so leave till next restart
1614 {
1615 ILandObject parcel = m_scene.LandChannel.GetLandObject(
1616 m_rootPart.GroupPosition.X, m_rootPart.GroupPosition.Y);
1617
1618 if (parcel != null && parcel.LandData != null &&
1619 parcel.LandData.OtherCleanTime != 0)
1620 {
1621 if (parcel.LandData.OwnerID != OwnerID &&
1622 (parcel.LandData.GroupID != GroupID ||
1623 parcel.LandData.GroupID == UUID.Zero))
1624 {
1625 if ((DateTime.UtcNow - RootPart.Rezzed).TotalMinutes >
1626 parcel.LandData.OtherCleanTime)
1627 {
1628 DetachFromBackup();
1629 m_log.DebugFormat(
1630 "[SCENE OBJECT GROUP]: Returning object {0} due to parcel autoreturn",
1631 RootPart.UUID);
1632 m_scene.AddReturn(OwnerID == GroupID ? LastOwnerID : OwnerID, Name, AbsolutePosition, "parcel autoreturn");
1633 m_scene.DeRezObjects(null, new List<uint>() { RootPart.LocalId }, UUID.Zero,
1634 DeRezAction.Return, UUID.Zero);
1635
1636 return;
1637 }
1638 }
1639 }
1640 }
1641
1642 if (m_scene.UseBackup && HasGroupChanged)
1643 {
1644 // don't backup while it's selected or you're asking for changes mid stream.
1645 if (isTimeToPersist() || forcedBackup)
1646 {
1647// m_log.DebugFormat(
1648// "[SCENE]: Storing {0}, {1} in {2}",
1649// Name, UUID, m_scene.RegionInfo.RegionName);
1650
1651 SceneObjectGroup backup_group = Copy(false);
1652 backup_group.RootPart.Velocity = RootPart.Velocity;
1653 backup_group.RootPart.Acceleration = RootPart.Acceleration;
1654 backup_group.RootPart.AngularVelocity = RootPart.AngularVelocity;
1655 backup_group.RootPart.ParticleSystem = RootPart.ParticleSystem;
1656 HasGroupChanged = false;
1657 GroupContainsForeignPrims = false;
1658
1659 m_scene.EventManager.TriggerOnSceneObjectPreSave(backup_group, this);
1660 datastore.StoreObject(backup_group, m_scene.RegionInfo.RegionID);
1661
1662 backup_group.ForEachPart(delegate(SceneObjectPart part)
1663 {
1664 part.Inventory.ProcessInventoryBackup(datastore);
1665 });
1666
1667 backup_group = null;
1668 }
1669// else
1670// {
1671// m_log.DebugFormat(
1672// "[SCENE]: Did not update persistence of object {0} {1}, selected = {2}",
1673// Name, UUID, IsSelected);
1674// }
1675 }
1676 }
1677 catch (Exception e)
1678 {
1679 m_log.ErrorFormat(
1680 "[SCENE]: Storing of {0}, {1} in {2} failed with exception {3}{4}",
1681 Name, UUID, m_scene.RegionInfo.RegionName, e.Message, e.StackTrace);
1682 }
1683 }
1684
1685 #endregion
1686
1687 /// <summary>
1688 /// Send the parts of this SOG to a single client
1689 /// </summary>
1690 /// <remarks>
1691 /// Used when the client initially connects and when client sends RequestPrim packet
1692 /// </remarks>
1693 /// <param name="remoteClient"></param>
1694 public void SendFullUpdateToClient(IClientAPI remoteClient)
1695 {
1696 RootPart.SendFullUpdate(remoteClient);
1697
1698 SceneObjectPart[] parts = m_parts.GetArray();
1699 for (int i = 0; i < parts.Length; i++)
1700 {
1701 SceneObjectPart part = parts[i];
1702 if (part != RootPart)
1703 part.SendFullUpdate(remoteClient);
1704 }
1705 }
1706
1707 #region Copying
1708
1709 /// <summary>
1710 /// Duplicates this object, including operations such as physics set up and attaching to the backup event.
1711 /// </summary>
1712 /// <param name="userExposed">True if the duplicate will immediately be in the scene, false otherwise</param>
1713 /// <returns></returns>
1714 public SceneObjectGroup Copy(bool userExposed)
1715 {
1716 // FIXME: This is dangerous since it's easy to forget to reset some references when necessary and end up
1717 // with bugs that only occur in some circumstances (e.g. crossing between regions on the same simulator
1718 // but not between regions on different simulators). Really, all copying should be done explicitly.
1719 SceneObjectGroup dupe = (SceneObjectGroup)MemberwiseClone();
1720
1721 dupe.Backup = false;
1722 dupe.m_parts = new MapAndArray<OpenMetaverse.UUID, SceneObjectPart>();
1723 dupe.m_sittingAvatars = new List<ScenePresence>();
1724 dupe.CopyRootPart(m_rootPart, OwnerID, GroupID, userExposed);
1725 dupe.m_rootPart.LinkNum = m_rootPart.LinkNum;
1726
1727 if (userExposed)
1728 dupe.m_rootPart.TrimPermissions();
1729
1730 List<SceneObjectPart> partList = new List<SceneObjectPart>(m_parts.GetArray());
1731
1732 partList.Sort(delegate(SceneObjectPart p1, SceneObjectPart p2)
1733 {
1734 return p1.LinkNum.CompareTo(p2.LinkNum);
1735 }
1736 );
1737
1738 foreach (SceneObjectPart part in partList)
1739 {
1740 SceneObjectPart newPart;
1741 if (part.UUID != m_rootPart.UUID)
1742 {
1743 newPart = dupe.CopyPart(part, OwnerID, GroupID, userExposed);
1744 newPart.LinkNum = part.LinkNum;
1745 }
1746 else
1747 {
1748 newPart = dupe.m_rootPart;
1749 }
1750
1751 // Need to duplicate the physics actor as well
1752 PhysicsActor originalPartPa = part.PhysActor;
1753 if (originalPartPa != null && userExposed)
1754 {
1755 PrimitiveBaseShape pbs = newPart.Shape;
1756
1757 newPart.PhysActor
1758 = m_scene.PhysicsScene.AddPrimShape(
1759 string.Format("{0}/{1}", newPart.Name, newPart.UUID),
1760 pbs,
1761 newPart.AbsolutePosition,
1762 newPart.Scale,
1763 newPart.RotationOffset,
1764 originalPartPa.IsPhysical,
1765 newPart.LocalId);
1766
1767 newPart.DoPhysicsPropertyUpdate(originalPartPa.IsPhysical, true);
1768 }
1769 if (part.KeyframeMotion != null)
1770 newPart.KeyframeMotion = part.KeyframeMotion.Copy(dupe);
1771 }
1772
1773 if (userExposed)
1774 {
1775 dupe.UpdateParentIDs();
1776 dupe.HasGroupChanged = true;
1777 dupe.AttachToBackup();
1778
1779 ScheduleGroupForFullUpdate();
1780 }
1781
1782 return dupe;
1783 }
1784
1785 /// <summary>
1786 /// Copy the given part as the root part of this scene object.
1787 /// </summary>
1788 /// <param name="part"></param>
1789 /// <param name="cAgentID"></param>
1790 /// <param name="cGroupID"></param>
1791 public void CopyRootPart(SceneObjectPart part, UUID cAgentID, UUID cGroupID, bool userExposed)
1792 {
1793 SetRootPart(part.Copy(m_scene.AllocateLocalId(), OwnerID, GroupID, 0, userExposed));
1794 }
1795
1796 public void ScriptSetPhysicsStatus(bool usePhysics)
1797 {
1798 if (usePhysics)
1799 {
1800 if (RootPart.KeyframeMotion != null)
1801 RootPart.KeyframeMotion.Stop();
1802 RootPart.KeyframeMotion = null;
1803 }
1804 UpdatePrimFlags(RootPart.LocalId, usePhysics, IsTemporary, IsPhantom, IsVolumeDetect);
1805 }
1806
1807 public void ScriptSetTemporaryStatus(bool makeTemporary)
1808 {
1809 UpdatePrimFlags(RootPart.LocalId, UsesPhysics, makeTemporary, IsPhantom, IsVolumeDetect);
1810 }
1811
1812 public void ScriptSetPhantomStatus(bool makePhantom)
1813 {
1814 UpdatePrimFlags(RootPart.LocalId, UsesPhysics, IsTemporary, makePhantom, IsVolumeDetect);
1815 }
1816
1817 public void ScriptSetVolumeDetect(bool makeVolumeDetect)
1818 {
1819 UpdatePrimFlags(RootPart.LocalId, UsesPhysics, IsTemporary, IsPhantom, makeVolumeDetect);
1820
1821 /*
1822 ScriptSetPhantomStatus(false); // What ever it was before, now it's not phantom anymore
1823
1824 if (PhysActor != null) // Should always be the case now
1825 {
1826 PhysActor.SetVolumeDetect(param);
1827 }
1828 if (param != 0)
1829 AddFlag(PrimFlags.Phantom);
1830
1831 ScheduleFullUpdate();
1832 */
1833 }
1834
1835 public void applyImpulse(Vector3 impulse)
1836 {
1837 if (IsAttachment)
1838 {
1839 ScenePresence avatar = m_scene.GetScenePresence(AttachedAvatar);
1840 if (avatar != null)
1841 {
1842 avatar.PushForce(impulse);
1843 }
1844 }
1845 else
1846 {
1847 PhysicsActor pa = RootPart.PhysActor;
1848
1849 if (pa != null)
1850 {
1851 pa.AddForce(impulse, true);
1852 m_scene.PhysicsScene.AddPhysicsActorTaint(pa);
1853 }
1854 }
1855 }
1856
1857 public void applyAngularImpulse(Vector3 impulse)
1858 {
1859 PhysicsActor pa = RootPart.PhysActor;
1860
1861 if (pa != null)
1862 {
1863 if (!IsAttachment)
1864 {
1865 pa.AddAngularForce(impulse, true);
1866 m_scene.PhysicsScene.AddPhysicsActorTaint(pa);
1867 }
1868 }
1869 }
1870
1871 public void setAngularImpulse(Vector3 impulse)
1872 {
1873 PhysicsActor pa = RootPart.PhysActor;
1874
1875 if (pa != null)
1876 {
1877 if (!IsAttachment)
1878 {
1879 pa.Torque = impulse;
1880 m_scene.PhysicsScene.AddPhysicsActorTaint(pa);
1881 }
1882 }
1883 }
1884
1885 public Vector3 GetTorque()
1886 {
1887 PhysicsActor pa = RootPart.PhysActor;
1888
1889 if (pa != null)
1890 {
1891 if (!IsAttachment)
1892 {
1893 Vector3 torque = pa.Torque;
1894 return torque;
1895 }
1896 }
1897
1898 return Vector3.Zero;
1899 }
1900
1901 public void MoveToTarget(Vector3 target, float tau)
1902 {
1903 if (IsAttachment)
1904 {
1905 ScenePresence avatar = m_scene.GetScenePresence(AttachedAvatar);
1906
1907 if (avatar != null)
1908 avatar.MoveToTarget(target, false, false);
1909 }
1910 else
1911 {
1912 PhysicsActor pa = RootPart.PhysActor;
1913
1914 if (pa != null)
1915 {
1916 pa.PIDTarget = target;
1917 pa.PIDTau = tau;
1918 pa.PIDActive = true;
1919 }
1920 }
1921 }
1922
1923 public void StopMoveToTarget()
1924 {
1925 if (IsAttachment)
1926 {
1927 ScenePresence avatar = m_scene.GetScenePresence(AttachedAvatar);
1928
1929 if (avatar != null)
1930 avatar.ResetMoveToTarget();
1931 }
1932 else
1933 {
1934 PhysicsActor pa = RootPart.PhysActor;
1935
1936 if (pa != null && pa.PIDActive)
1937 {
1938 pa.PIDActive = false;
1939
1940 ScheduleGroupForTerseUpdate();
1941 }
1942 }
1943 }
1944
1945 /// <summary>
1946 /// Uses a PID to attempt to clamp the object on the Z axis at the given height over tau seconds.
1947 /// </summary>
1948 /// <param name="height">Height to hover. Height of zero disables hover.</param>
1949 /// <param name="hoverType">Determines what the height is relative to </param>
1950 /// <param name="tau">Number of seconds over which to reach target</param>
1951 public void SetHoverHeight(float height, PIDHoverType hoverType, float tau)
1952 {
1953 PhysicsActor pa = RootPart.PhysActor;
1954
1955 if (pa != null)
1956 {
1957 if (height != 0f)
1958 {
1959 pa.PIDHoverHeight = height;
1960 pa.PIDHoverType = hoverType;
1961 pa.PIDTau = tau;
1962 pa.PIDHoverActive = true;
1963 }
1964 else
1965 {
1966 pa.PIDHoverActive = false;
1967 }
1968 }
1969 }
1970
1971 /// <summary>
1972 /// Set the owner of the root part.
1973 /// </summary>
1974 /// <param name="part"></param>
1975 /// <param name="cAgentID"></param>
1976 /// <param name="cGroupID"></param>
1977 public void SetRootPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID)
1978 {
1979 part.LastOwnerID = part.OwnerID;
1980 part.OwnerID = cAgentID;
1981 part.GroupID = cGroupID;
1982
1983 if (part.OwnerID != cAgentID)
1984 {
1985 // Apply Next Owner Permissions if we're not bypassing permissions
1986 if (!m_scene.Permissions.BypassPermissions())
1987 ApplyNextOwnerPermissions();
1988 }
1989
1990 part.ScheduleFullUpdate();
1991 }
1992
1993 /// <summary>
1994 /// Make a copy of the given part.
1995 /// </summary>
1996 /// <param name="part"></param>
1997 /// <param name="cAgentID"></param>
1998 /// <param name="cGroupID"></param>
1999 public SceneObjectPart CopyPart(SceneObjectPart part, UUID cAgentID, UUID cGroupID, bool userExposed)
2000 {
2001 SceneObjectPart newPart = part.Copy(m_scene.AllocateLocalId(), OwnerID, GroupID, m_parts.Count, userExposed);
2002 AddPart(newPart);
2003
2004 SetPartAsNonRoot(newPart);
2005 return newPart;
2006 }
2007
2008 /// <summary>
2009 /// Reset the UUIDs for all the prims that make up this group.
2010 /// </summary>
2011 /// <remarks>
2012 /// This is called by methods which want to add a new group to an existing scene, in order
2013 /// to ensure that there are no clashes with groups already present.
2014 /// </remarks>
2015 public void ResetIDs()
2016 {
2017 lock (m_parts.SyncRoot)
2018 {
2019 List<SceneObjectPart> partsList = new List<SceneObjectPart>(m_parts.GetArray());
2020 m_parts.Clear();
2021 foreach (SceneObjectPart part in partsList)
2022 {
2023 part.ResetIDs(part.LinkNum); // Don't change link nums
2024 m_parts.Add(part.UUID, part);
2025 }
2026 }
2027 }
2028
2029 /// <summary>
2030 ///
2031 /// </summary>
2032 /// <param name="part"></param>
2033 public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags)
2034 {
2035 remoteClient.SendObjectPropertiesFamilyData(RootPart, RequestFlags);
2036
2037// remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask,
2038// RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask,
2039// RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category,
2040// RootPart.CreatorID, RootPart.Name, RootPart.Description);
2041 }
2042
2043 public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID)
2044 {
2045 part.OwnerID = cAgentID;
2046 part.GroupID = cGroupID;
2047 }
2048
2049 #endregion
2050
2051 public override void Update()
2052 {
2053 // Check that the group was not deleted before the scheduled update
2054 // FIXME: This is merely a temporary measure to reduce the incidence of failure when
2055 // an object has been deleted from a scene before update was processed.
2056 // A more fundamental overhaul of the update mechanism is required to eliminate all
2057 // the race conditions.
2058 if (IsDeleted)
2059 return;
2060
2061 // Even temporary objects take part in physics (e.g. temp-on-rez bullets)
2062 //if ((RootPart.Flags & PrimFlags.TemporaryOnRez) != 0)
2063 // return;
2064
2065 // If we somehow got here to updating the SOG and its root part is not scheduled for update,
2066 // check to see if the physical position or rotation warrant an update.
2067 if (m_rootPart.UpdateFlag == UpdateRequired.NONE)
2068 {
2069 bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0);
2070
2071 if (UsePhysics && !AbsolutePosition.ApproxEquals(lastPhysGroupPos, 0.02f))
2072 {
2073 m_rootPart.UpdateFlag = UpdateRequired.TERSE;
2074 lastPhysGroupPos = AbsolutePosition;
2075 }
2076
2077 if (UsePhysics && !GroupRotation.ApproxEquals(lastPhysGroupRot, 0.1f))
2078 {
2079 m_rootPart.UpdateFlag = UpdateRequired.TERSE;
2080 lastPhysGroupRot = GroupRotation;
2081 }
2082 }
2083
2084 SceneObjectPart[] parts = m_parts.GetArray();
2085 for (int i = 0; i < parts.Length; i++)
2086 {
2087 SceneObjectPart part = parts[i];
2088 if (!IsSelected)
2089 part.UpdateLookAt();
2090 part.SendScheduledUpdates();
2091 }
2092 }
2093
2094 /// <summary>
2095 /// Schedule a full update for this scene object to all interested viewers.
2096 /// </summary>
2097 /// <remarks>
2098 /// Ultimately, this should be managed such that region modules can invoke it at the end of a set of operations
2099 /// so that either all changes are sent at once. However, currently, a large amount of internal
2100 /// code will set this anyway when some object properties are changed.
2101 /// </remarks>
2102 public void ScheduleGroupForFullUpdate()
2103 {
2104// if (IsAttachment)
2105// m_log.DebugFormat("[SOG]: Scheduling full update for {0} {1}", Name, LocalId);
2106
2107 checkAtTargets();
2108 RootPart.ScheduleFullUpdate();
2109
2110 SceneObjectPart[] parts = m_parts.GetArray();
2111 for (int i = 0; i < parts.Length; i++)
2112 {
2113 SceneObjectPart part = parts[i];
2114 if (part != RootPart)
2115 part.ScheduleFullUpdate();
2116 }
2117 }
2118
2119 /// <summary>
2120 /// Schedule a terse update for this scene object to all interested viewers.
2121 /// </summary>
2122 /// <remarks>
2123 /// Ultimately, this should be managed such that region modules can invoke it at the end of a set of operations
2124 /// so that either all changes are sent at once. However, currently, a large amount of internal
2125 /// code will set this anyway when some object properties are changed.
2126 /// </remarks>
2127 public void ScheduleGroupForTerseUpdate()
2128 {
2129// m_log.DebugFormat("[SOG]: Scheduling terse update for {0} {1}", Name, UUID);
2130
2131 SceneObjectPart[] parts = m_parts.GetArray();
2132 for (int i = 0; i < parts.Length; i++)
2133 parts[i].ScheduleTerseUpdate();
2134 }
2135
2136 /// <summary>
2137 /// Immediately send a full update for this scene object.
2138 /// </summary>
2139 public void SendGroupFullUpdate()
2140 {
2141 if (IsDeleted)
2142 return;
2143
2144// m_log.DebugFormat("[SOG]: Sending immediate full group update for {0} {1}", Name, UUID);
2145
2146 RootPart.SendFullUpdateToAllClients();
2147
2148 SceneObjectPart[] parts = m_parts.GetArray();
2149 for (int i = 0; i < parts.Length; i++)
2150 {
2151 SceneObjectPart part = parts[i];
2152 if (part != RootPart)
2153 part.SendFullUpdateToAllClients();
2154 }
2155 }
2156
2157 /// <summary>
2158 /// Immediately send an update for this scene object's root prim only.
2159 /// This is for updates regarding the object as a whole, and none of its parts in particular.
2160 /// Note: this may not be used by opensim (it probably should) but it's used by
2161 /// external modules.
2162 /// </summary>
2163 public void SendGroupRootTerseUpdate()
2164 {
2165 if (IsDeleted)
2166 return;
2167
2168 RootPart.SendTerseUpdateToAllClients();
2169 }
2170
2171 public void QueueForUpdateCheck()
2172 {
2173 if (m_scene == null) // Need to check here as it's null during object creation
2174 return;
2175
2176 m_scene.SceneGraph.AddToUpdateList(this);
2177 }
2178
2179 /// <summary>
2180 /// Immediately send a terse update for this scene object.
2181 /// </summary>
2182 public void SendGroupTerseUpdate()
2183 {
2184 if (IsDeleted)
2185 return;
2186
2187 SceneObjectPart[] parts = m_parts.GetArray();
2188 for (int i = 0; i < parts.Length; i++)
2189 parts[i].SendTerseUpdateToAllClients();
2190 }
2191
2192 /// <summary>
2193 /// Send metadata about the root prim (name, description, sale price, permissions, etc.) to a client.
2194 /// </summary>
2195 /// <param name="client"></param>
2196 public void SendPropertiesToClient(IClientAPI client)
2197 {
2198 m_rootPart.SendPropertiesToClient(client);
2199 }
2200
2201 #region SceneGroupPart Methods
2202
2203 /// <summary>
2204 /// Get the child part by LinkNum
2205 /// </summary>
2206 /// <param name="linknum"></param>
2207 /// <returns>null if no child part with that linknum or child part</returns>
2208 public SceneObjectPart GetLinkNumPart(int linknum)
2209 {
2210 SceneObjectPart[] parts = m_parts.GetArray();
2211 for (int i = 0; i < parts.Length; i++)
2212 {
2213 if (parts[i].LinkNum == linknum)
2214 return parts[i];
2215 }
2216
2217 return null;
2218 }
2219
2220 /// <summary>
2221 /// Get a part with a given UUID
2222 /// </summary>
2223 /// <param name="primID"></param>
2224 /// <returns>null if a part with the primID was not found</returns>
2225 public SceneObjectPart GetPart(UUID primID)
2226 {
2227 SceneObjectPart childPart;
2228 m_parts.TryGetValue(primID, out childPart);
2229 return childPart;
2230 }
2231
2232 /// <summary>
2233 /// Get a part with a given local ID
2234 /// </summary>
2235 /// <param name="localID"></param>
2236 /// <returns>null if a part with the local ID was not found</returns>
2237 public SceneObjectPart GetPart(uint localID)
2238 {
2239 SceneObjectPart[] parts = m_parts.GetArray();
2240 for (int i = 0; i < parts.Length; i++)
2241 {
2242 if (parts[i].LocalId == localID)
2243 return parts[i];
2244 }
2245
2246 return null;
2247 }
2248
2249 #endregion
2250
2251 #region Packet Handlers
2252
2253 /// <summary>
2254 /// Link the prims in a given group to this group
2255 /// </summary>
2256 /// <remarks>
2257 /// Do not call this method directly - use Scene.LinkObjects() instead to avoid races between threads.
2258 /// FIXME: There are places where scripts call these methods directly without locking. This is a potential race condition.
2259 /// </remarks>
2260 /// <param name="objectGroup">The group of prims which should be linked to this group</param>
2261 public void LinkToGroup(SceneObjectGroup objectGroup)
2262 {
2263 LinkToGroup(objectGroup, false);
2264 }
2265
2266 // Link an existing group to this group.
2267 // The group being linked need not be a linkset -- it can have just one prim.
2268 public void LinkToGroup(SceneObjectGroup objectGroup, bool insert)
2269 {
2270// m_log.DebugFormat(
2271// "[SCENE OBJECT GROUP]: Linking group with root part {0}, {1} to group with root part {2}, {3}",
2272// objectGroup.RootPart.Name, objectGroup.RootPart.UUID, RootPart.Name, RootPart.UUID);
2273
2274 // Linking to ourselves is not a valid operation.
2275 if (objectGroup == this)
2276 return;
2277
2278 // If the configured linkset capacity is greater than zero,
2279 // and the new linkset would have a prim count higher than this
2280 // value, do not link it.
2281 if (m_scene.m_linksetCapacity > 0 &&
2282 (PrimCount + objectGroup.PrimCount) >
2283 m_scene.m_linksetCapacity)
2284 {
2285 m_log.DebugFormat(
2286 "[SCENE OBJECT GROUP]: Cannot link group with root" +
2287 " part {0}, {1} ({2} prims) to group with root part" +
2288 " {3}, {4} ({5} prims) because the new linkset" +
2289 " would exceed the configured maximum of {6}",
2290 objectGroup.RootPart.Name, objectGroup.RootPart.UUID,
2291 objectGroup.PrimCount, RootPart.Name, RootPart.UUID,
2292 PrimCount, m_scene.m_linksetCapacity);
2293
2294 return;
2295 }
2296
2297 // 'linkPart' == the root of the group being linked into this group
2298 SceneObjectPart linkPart = objectGroup.m_rootPart;
2299
2300 // physics flags from group to be applied to linked parts
2301 bool grpusephys = UsesPhysics;
2302 bool grptemporary = IsTemporary;
2303
2304 // Remember where the group being linked thought it was
2305 Vector3 oldGroupPosition = linkPart.GroupPosition;
2306 Quaternion oldRootRotation = linkPart.RotationOffset;
2307
2308 // A linked SOP remembers its location and rotation relative to the root of a group.
2309 // Convert the root of the group being linked to be relative to the
2310 // root of the group being linked to.
2311 // Note: Some of the assignments have complex side effects.
2312
2313 // First move the new group's root SOP's position to be relative to ours
2314 // (radams1: Not sure if the multiple setting of OffsetPosition is required. If not,
2315 // this code can be reordered to have a more logical flow.)
2316 linkPart.OffsetPosition = linkPart.GroupPosition - AbsolutePosition;
2317 // Assign the new parent to the root of the old group
2318 linkPart.ParentID = m_rootPart.LocalId;
2319 // Now that it's a child, it's group position is our root position
2320 linkPart.GroupPosition = AbsolutePosition;
2321
2322 Vector3 axPos = linkPart.OffsetPosition;
2323 // Rotate the linking root SOP's position to be relative to the new root prim
2324 Quaternion parentRot = m_rootPart.RotationOffset;
2325 axPos *= Quaternion.Inverse(parentRot);
2326 linkPart.OffsetPosition = axPos;
2327
2328 // Make the linking root SOP's rotation relative to the new root prim
2329 Quaternion oldRot = linkPart.RotationOffset;
2330 Quaternion newRot = Quaternion.Inverse(parentRot) * oldRot;
2331 linkPart.RotationOffset = newRot;
2332
2333 // If there is only one SOP in a SOG, the LinkNum is zero. I.e., not a linkset.
2334 // Now that we know this SOG has at least two SOPs in it, the new root
2335 // SOP becomes the first in the linkset.
2336 if (m_rootPart.LinkNum == 0)
2337 m_rootPart.LinkNum = 1;
2338
2339 lock (m_parts.SyncRoot)
2340 {
2341 // Calculate the new link number for the old root SOP
2342 int linkNum;
2343 if (insert)
2344 {
2345 linkNum = 2;
2346 foreach (SceneObjectPart part in Parts)
2347 {
2348 if (part.LinkNum > 1)
2349 part.LinkNum++;
2350 }
2351 }
2352 else
2353 {
2354 linkNum = PrimCount + 1;
2355 }
2356
2357 // Add the old root SOP as a part in our group's list
2358 m_parts.Add(linkPart.UUID, linkPart);
2359
2360 linkPart.SetParent(this);
2361 linkPart.CreateSelected = true;
2362
2363 // let physics know preserve part volume dtc messy since UpdatePrimFlags doesn't look to parent changes for now
2364 linkPart.UpdatePrimFlags(grpusephys, grptemporary, (IsPhantom || (linkPart.Flags & PrimFlags.Phantom) != 0), linkPart.VolumeDetectActive);
2365
2366 // If the added SOP is physical, also tell the physics engine about the link relationship.
2367 if (linkPart.PhysActor != null && m_rootPart.PhysActor != null && m_rootPart.PhysActor.IsPhysical)
2368 {
2369 linkPart.PhysActor.link(m_rootPart.PhysActor);
2370 this.Scene.PhysicsScene.AddPhysicsActorTaint(linkPart.PhysActor);
2371 }
2372
2373 linkPart.LinkNum = linkNum++;
2374
2375 // Get a list of the SOP's in the old group in order of their linknum's.
2376 SceneObjectPart[] ogParts = objectGroup.Parts;
2377 Array.Sort(ogParts, delegate(SceneObjectPart a, SceneObjectPart b)
2378 {
2379 return a.LinkNum - b.LinkNum;
2380 });
2381
2382 // Add each of the SOP's from the old linkset to our linkset
2383 for (int i = 0; i < ogParts.Length; i++)
2384 {
2385 SceneObjectPart part = ogParts[i];
2386 if (part.UUID != objectGroup.m_rootPart.UUID)
2387 {
2388 LinkNonRootPart(part, oldGroupPosition, oldRootRotation, linkNum++);
2389
2390 // Update the physics flags for the newly added SOP
2391 // (Is this necessary? LinkNonRootPart() has already called UpdatePrimFlags but with different flags!??)
2392 part.UpdatePrimFlags(grpusephys, grptemporary, (IsPhantom || (part.Flags & PrimFlags.Phantom) != 0), part.VolumeDetectActive);
2393
2394 // If the added SOP is physical, also tell the physics engine about the link relationship.
2395 if (part.PhysActor != null && m_rootPart.PhysActor != null && m_rootPart.PhysActor.IsPhysical)
2396 {
2397 part.PhysActor.link(m_rootPart.PhysActor);
2398 this.Scene.PhysicsScene.AddPhysicsActorTaint(part.PhysActor);
2399 }
2400 }
2401 part.ClearUndoState();
2402 }
2403 }
2404
2405 // Now that we've aquired all of the old SOG's parts, remove the old SOG from the scene.
2406 m_scene.UnlinkSceneObject(objectGroup, true);
2407 objectGroup.IsDeleted = true;
2408
2409 objectGroup.m_parts.Clear();
2410
2411 // Can't do this yet since backup still makes use of the root part without any synchronization
2412// objectGroup.m_rootPart = null;
2413
2414 // If linking prims with different permissions, fix them
2415 AdjustChildPrimPermissions(false);
2416
2417 GroupContainsForeignPrims = true;
2418
2419 AttachToBackup();
2420
2421 // Here's the deal, this is ABSOLUTELY CRITICAL so the physics scene gets the update about the
2422 // position of linkset prims. IF YOU CHANGE THIS, YOU MUST TEST colliding with just linked and
2423 // unmoved prims!
2424 ResetChildPrimPhysicsPositions();
2425
2426 //HasGroupChanged = true;
2427 //ScheduleGroupForFullUpdate();
2428 }
2429
2430 /// <summary>
2431 /// Delink the given prim from this group. The delinked prim is established as
2432 /// an independent SceneObjectGroup.
2433 /// </summary>
2434 /// <remarks>
2435 /// FIXME: This method should not be called directly since it bypasses update locking, allowing a potential race
2436 /// condition. But currently there is no
2437 /// alternative method that does take a lonk to delink a single prim.
2438 /// </remarks>
2439 /// <param name="partID"></param>
2440 /// <returns>The object group of the newly delinked prim. Null if part could not be found</returns>
2441 public SceneObjectGroup DelinkFromGroup(uint partID)
2442 {
2443 return DelinkFromGroup(partID, true);
2444 }
2445
2446 /// <summary>
2447 /// Delink the given prim from this group. The delinked prim is established as
2448 /// an independent SceneObjectGroup.
2449 /// </summary>
2450 /// <remarks>
2451 /// FIXME: This method should not be called directly since it bypasses update locking, allowing a potential race
2452 /// condition. But currently there is no
2453 /// alternative method that does take a lonk to delink a single prim.
2454 /// </remarks>
2455 /// <param name="partID"></param>
2456 /// <param name="sendEvents"></param>
2457 /// <returns>The object group of the newly delinked prim. Null if part could not be found</returns>
2458 public SceneObjectGroup DelinkFromGroup(uint partID, bool sendEvents)
2459 {
2460 SceneObjectPart linkPart = GetPart(partID);
2461
2462 if (linkPart != null)
2463 {
2464 return DelinkFromGroup(linkPart, sendEvents);
2465 }
2466 else
2467 {
2468 m_log.WarnFormat("[SCENE OBJECT GROUP]: " +
2469 "DelinkFromGroup(): Child prim {0} not found in object {1}, {2}",
2470 partID, LocalId, UUID);
2471
2472 return null;
2473 }
2474 }
2475
2476 /// <summary>
2477 /// Delink the given prim from this group. The delinked prim is established as
2478 /// an independent SceneObjectGroup.
2479 /// </summary>
2480 /// <remarks>
2481 /// FIXME: This method should not be called directly since it bypasses update locking, allowing a potential race
2482 /// condition. But currently there is no
2483 /// alternative method that does take a lock to delink a single prim.
2484 /// </remarks>
2485 /// <param name="partID"></param>
2486 /// <param name="sendEvents"></param>
2487 /// <returns>The object group of the newly delinked prim.</returns>
2488 public SceneObjectGroup DelinkFromGroup(SceneObjectPart linkPart, bool sendEvents)
2489 {
2490// m_log.DebugFormat(
2491// "[SCENE OBJECT GROUP]: Delinking part {0}, {1} from group with root part {2}, {3}",
2492// linkPart.Name, linkPart.UUID, RootPart.Name, RootPart.UUID);
2493
2494 linkPart.ClearUndoState();
2495
2496 Vector3 worldPos = linkPart.GetWorldPosition();
2497 Quaternion worldRot = linkPart.GetWorldRotation();
2498
2499 // Remove the part from this object
2500 lock (m_parts.SyncRoot)
2501 {
2502 m_parts.Remove(linkPart.UUID);
2503
2504 SceneObjectPart[] parts = m_parts.GetArray();
2505
2506 // Rejigger the linknum's of the remaining SOP's to fill any gap
2507 if (parts.Length == 1 && RootPart != null)
2508 {
2509 // Single prim left
2510 RootPart.LinkNum = 0;
2511 }
2512 else
2513 {
2514 for (int i = 0; i < parts.Length; i++)
2515 {
2516 SceneObjectPart part = parts[i];
2517 if (part.LinkNum > linkPart.LinkNum)
2518 part.LinkNum--;
2519 }
2520 }
2521 }
2522
2523 linkPart.ParentID = 0;
2524 linkPart.LinkNum = 0;
2525
2526 PhysicsActor linkPartPa = linkPart.PhysActor;
2527
2528 // Remove the SOP from the physical scene.
2529 // If the new SOG is physical, it is re-created later.
2530 // (There is a problem here in that we have not yet told the physics
2531 // engine about the delink. Someday, linksets should be made first
2532 // class objects in the physics engine interface).
2533 if (linkPartPa != null)
2534 m_scene.PhysicsScene.RemovePrim(linkPartPa);
2535
2536 // We need to reset the child part's position
2537 // ready for life as a separate object after being a part of another object
2538
2539 /* This commented out code seems to recompute what GetWorldPosition already does.
2540 * Replace with a call to GetWorldPosition (before unlinking)
2541 Quaternion parentRot = m_rootPart.RotationOffset;
2542 Vector3 axPos = linkPart.OffsetPosition;
2543 axPos *= parentRot;
2544 linkPart.OffsetPosition = new Vector3(axPos.X, axPos.Y, axPos.Z);
2545 linkPart.GroupPosition = AbsolutePosition + linkPart.OffsetPosition;
2546 linkPart.OffsetPosition = new Vector3(0, 0, 0);
2547 */
2548 linkPart.GroupPosition = worldPos;
2549 linkPart.OffsetPosition = Vector3.Zero;
2550 linkPart.RotationOffset = worldRot;
2551
2552 // Create a new SOG to go around this unlinked and unattached SOP
2553 SceneObjectGroup objectGroup = new SceneObjectGroup(linkPart);
2554
2555 m_scene.AddNewSceneObject(objectGroup, true);
2556
2557 if (sendEvents)
2558 linkPart.TriggerScriptChangedEvent(Changed.LINK);
2559
2560 linkPart.Rezzed = RootPart.Rezzed;
2561
2562 // We must persist the delinked group to the database immediately, for safety. The problem
2563 // is that although in memory the new group has a new SceneGroupID, in the database it
2564 // still has the parent group's SceneGroupID (until the next backup). This means that if the
2565 // parent group is deleted then the delinked group will also be deleted from the database.
2566 // This problem will disappear if the region remains alive long enough for another backup,
2567 // since at that time the delinked group's new SceneGroupID will be written to the database.
2568 // But if the region crashes before that then the prims will be permanently gone, and this must
2569 // not happen. (We can't use a just-in-time trick like GroupContainsForeignPrims in this case
2570 // because the delinked group doesn't know when the source group is deleted.)
2571 m_scene.ForceSceneObjectBackup(objectGroup);
2572
2573 return objectGroup;
2574 }
2575
2576 /// <summary>
2577 /// Stop this object from being persisted over server restarts.
2578 /// </summary>
2579 /// <param name="objectGroup"></param>
2580 public virtual void DetachFromBackup()
2581 {
2582 if (Backup && Scene != null)
2583 m_scene.EventManager.OnBackup -= ProcessBackup;
2584
2585 Backup = false;
2586 }
2587
2588 // This links an SOP from a previous linkset into my linkset.
2589 // The trick is that the SOP's position and rotation are relative to the old root SOP's
2590 // so we are passed in the position and rotation of the old linkset so this can
2591 // unjigger this SOP's position and rotation from the previous linkset and
2592 // then make them relative to my linkset root.
2593 private void LinkNonRootPart(SceneObjectPart part, Vector3 oldGroupPosition, Quaternion oldGroupRotation, int linkNum)
2594 {
2595 Quaternion parentRot = oldGroupRotation;
2596 Quaternion oldRot = part.RotationOffset;
2597
2598 // Move our position to not be relative to the old parent
2599 Vector3 axPos = part.OffsetPosition;
2600 axPos *= parentRot;
2601 part.OffsetPosition = axPos;
2602 part.GroupPosition = oldGroupPosition + part.OffsetPosition;
2603 part.OffsetPosition = Vector3.Zero;
2604
2605 // Compution our rotation to be not relative to the old parent
2606 Quaternion worldRot = parentRot * oldRot;
2607 part.RotationOffset = worldRot;
2608
2609 // Add this SOP to our linkset
2610 part.SetParent(this);
2611 part.ParentID = m_rootPart.LocalId;
2612 m_parts.Add(part.UUID, part);
2613
2614 part.LinkNum = linkNum;
2615
2616 // Compute the new position of this SOP relative to the group position
2617 part.OffsetPosition = part.GroupPosition - AbsolutePosition;
2618
2619 // (radams1 20120711: I don't know why part.OffsetPosition is set multiple times.
2620 // It would have the affect of setting the physics engine position multiple
2621 // times. In theory, that is not necessary but I don't have a good linkset
2622 // test to know that cleaning up this code wouldn't break things.)
2623
2624 // Rotate the relative position by the rotation of the group
2625 Quaternion rootRotation = m_rootPart.RotationOffset;
2626 Vector3 pos = part.OffsetPosition;
2627 pos *= Quaternion.Inverse(rootRotation);
2628 part.OffsetPosition = pos;
2629
2630 // Compute the SOP's rotation relative to the rotation of the group.
2631 parentRot = m_rootPart.RotationOffset;
2632 oldRot = part.RotationOffset;
2633 Quaternion newRot = Quaternion.Inverse(parentRot) * oldRot;
2634 part.RotationOffset = newRot;
2635
2636 // Since this SOP's state has changed, push those changes into the physics engine
2637 // and the simulator.
2638 part.UpdatePrimFlags(UsesPhysics, IsTemporary, IsPhantom, IsVolumeDetect);
2639 }
2640
2641 /// <summary>
2642 /// If object is physical, apply force to move it around
2643 /// If object is not physical, just put it at the resulting location
2644 /// </summary>
2645 /// <param name="partID">Part ID to check for grab</param>
2646 /// <param name="offset">Always seems to be 0,0,0, so ignoring</param>
2647 /// <param name="pos">New position. We do the math here to turn it into a force</param>
2648 /// <param name="remoteClient"></param>
2649 public void GrabMovement(UUID partID, Vector3 offset, Vector3 pos, IClientAPI remoteClient)
2650 {
2651 if (m_scene.EventManager.TriggerGroupMove(UUID, pos))
2652 {
2653 SceneObjectPart part = GetPart(partID);
2654
2655 if (part == null)
2656 return;
2657
2658 PhysicsActor pa = m_rootPart.PhysActor;
2659
2660 if (pa != null)
2661 {
2662 if (pa.IsPhysical)
2663 {
2664 if (!BlockGrabOverride && !part.BlockGrab)
2665 {
2666 Vector3 llmoveforce = pos - AbsolutePosition;
2667 Vector3 grabforce = llmoveforce;
2668 grabforce = (grabforce / 10) * pa.Mass;
2669 pa.AddForce(grabforce, true);
2670 m_scene.PhysicsScene.AddPhysicsActorTaint(pa);
2671 }
2672 }
2673 else
2674 {
2675 NonPhysicalGrabMovement(pos);
2676 }
2677 }
2678 else
2679 {
2680 NonPhysicalGrabMovement(pos);
2681 }
2682 }
2683 }
2684
2685 /// <summary>
2686 /// Apply possition for grabbing non-physical linksets (Ctrl+Drag)
2687 /// This MUST be blocked for linksets that contain touch scripts because the viewer triggers grab on the touch
2688 /// event (Viewer Bug?) This would allow anyone to drag a linkset with a touch script. SL behaviour is also to
2689 /// block grab on prims with touch events.
2690 /// </summary>
2691 /// <param name="pos">New Position</param>
2692 public void NonPhysicalGrabMovement(Vector3 pos)
2693 {
2694 if(!IsAttachment && ScriptCount() == 0)
2695 UpdateGroupPosition(pos);
2696 }
2697
2698 /// <summary>
2699 /// If object is physical, prepare for spinning torques (set flag to save old orientation)
2700 /// </summary>
2701 /// <param name="rotation">Rotation. We do the math here to turn it into a torque</param>
2702 /// <param name="remoteClient"></param>
2703 public void SpinStart(IClientAPI remoteClient)
2704 {
2705 if (m_scene.EventManager.TriggerGroupSpinStart(UUID))
2706 {
2707 PhysicsActor pa = m_rootPart.PhysActor;
2708
2709 if (pa != null)
2710 {
2711 if (pa.IsPhysical)
2712 {
2713 m_rootPart.IsWaitingForFirstSpinUpdatePacket = true;
2714 }
2715 }
2716 }
2717 }
2718
2719 /// <summary>
2720 /// If object is physical, apply torque to spin it around
2721 /// </summary>
2722 /// <param name="rotation">Rotation. We do the math here to turn it into a torque</param>
2723 /// <param name="remoteClient"></param>
2724 public void SpinMovement(Quaternion newOrientation, IClientAPI remoteClient)
2725 {
2726 // The incoming newOrientation, sent by the client, "seems" to be the
2727 // desired target orientation. This needs further verification; in particular,
2728 // one would expect that the initial incoming newOrientation should be
2729 // fairly close to the original prim's physical orientation,
2730 // m_rootPart.PhysActor.Orientation. This however does not seem to be the
2731 // case (might just be an issue with different quaternions representing the
2732 // same rotation, or it might be a coordinate system issue).
2733 //
2734 // Since it's not clear what the relationship is between the PhysActor.Orientation
2735 // and the incoming orientations sent by the client, we take an alternative approach
2736 // of calculating the delta rotation between the orientations being sent by the
2737 // client. (Since a spin is invoked by ctrl+shift+drag in the client, we expect
2738 // a steady stream of several new orientations coming in from the client.)
2739 // This ensures that the delta rotations are being calculated from self-consistent
2740 // pairs of old/new rotations. Given the delta rotation, we apply a torque around
2741 // the delta rotation axis, scaled by the object mass times an arbitrary scaling
2742 // factor (to ensure the resulting torque is not "too strong" or "too weak").
2743 //
2744 // Ideally we need to calculate (probably iteratively) the exact torque or series
2745 // of torques needed to arrive exactly at the destination orientation. However, since
2746 // it is not yet clear how to map the destination orientation (provided by the viewer)
2747 // into PhysActor orientations (needed by the physics engine), we omit this step.
2748 // This means that the resulting torque will at least be in the correct direction,
2749 // but it will result in over-shoot or under-shoot of the target orientation.
2750 // For the end user, this means that ctrl+shift+drag can be used for relative,
2751 // but not absolute, adjustments of orientation for physical prims.
2752 if (m_scene.EventManager.TriggerGroupSpin(UUID, newOrientation))
2753 {
2754 PhysicsActor pa = m_rootPart.PhysActor;
2755
2756 if (pa != null)
2757 {
2758 if (pa.IsPhysical)
2759 {
2760 if (m_rootPart.IsWaitingForFirstSpinUpdatePacket)
2761 {
2762 // first time initialization of "old" orientation for calculation of delta rotations
2763 m_rootPart.SpinOldOrientation = newOrientation;
2764 m_rootPart.IsWaitingForFirstSpinUpdatePacket = false;
2765 }
2766 else
2767 {
2768 // save and update old orientation
2769 Quaternion old = m_rootPart.SpinOldOrientation;
2770 m_rootPart.SpinOldOrientation = newOrientation;
2771 //m_log.Error("[SCENE OBJECT GROUP]: Old orientation is " + old);
2772 //m_log.Error("[SCENE OBJECT GROUP]: Incoming new orientation is " + newOrientation);
2773
2774 // compute difference between previous old rotation and new incoming rotation
2775 Quaternion minimalRotationFromQ1ToQ2 = Quaternion.Inverse(old) * newOrientation;
2776
2777 float rotationAngle;
2778 Vector3 rotationAxis;
2779 minimalRotationFromQ1ToQ2.GetAxisAngle(out rotationAxis, out rotationAngle);
2780 rotationAxis.Normalize();
2781
2782 //m_log.Error("SCENE OBJECT GROUP]: rotation axis is " + rotationAxis);
2783 Vector3 spinforce = new Vector3(rotationAxis.X, rotationAxis.Y, rotationAxis.Z);
2784 spinforce = (spinforce/8) * pa.Mass; // 8 is an arbitrary torque scaling factor
2785 pa.AddAngularForce(spinforce,true);
2786 m_scene.PhysicsScene.AddPhysicsActorTaint(pa);
2787 }
2788 }
2789 else
2790 {
2791 NonPhysicalSpinMovement(newOrientation);
2792 }
2793 }
2794 else
2795 {
2796 NonPhysicalSpinMovement(newOrientation);
2797 }
2798 }
2799 }
2800
2801 /// <summary>
2802 /// Apply rotation for spinning non-physical linksets (Ctrl+Shift+Drag)
2803 /// As with dragging, scripted objects must be blocked from spinning
2804 /// </summary>
2805 /// <param name="newOrientation">New Rotation</param>
2806 private void NonPhysicalSpinMovement(Quaternion newOrientation)
2807 {
2808 if(!IsAttachment && ScriptCount() == 0)
2809 UpdateGroupRotationR(newOrientation);
2810 }
2811
2812 /// <summary>
2813 /// Set the name of a prim
2814 /// </summary>
2815 /// <param name="name"></param>
2816 /// <param name="localID"></param>
2817 public void SetPartName(string name, uint localID)
2818 {
2819 SceneObjectPart part = GetPart(localID);
2820 if (part != null)
2821 {
2822 part.Name = name;
2823 }
2824 }
2825
2826 public void SetPartDescription(string des, uint localID)
2827 {
2828 SceneObjectPart part = GetPart(localID);
2829 if (part != null)
2830 {
2831 part.Description = des;
2832 }
2833 }
2834
2835 public void SetPartText(string text, uint localID)
2836 {
2837 SceneObjectPart part = GetPart(localID);
2838 if (part != null)
2839 {
2840 part.SetText(text);
2841 }
2842 }
2843
2844 public void SetPartText(string text, UUID partID)
2845 {
2846 SceneObjectPart part = GetPart(partID);
2847 if (part != null)
2848 {
2849 part.SetText(text);
2850 }
2851 }
2852
2853 public string GetPartName(uint localID)
2854 {
2855 SceneObjectPart part = GetPart(localID);
2856 if (part != null)
2857 {
2858 return part.Name;
2859 }
2860 return String.Empty;
2861 }
2862
2863 public string GetPartDescription(uint localID)
2864 {
2865 SceneObjectPart part = GetPart(localID);
2866 if (part != null)
2867 {
2868 return part.Description;
2869 }
2870 return String.Empty;
2871 }
2872
2873 /// <summary>
2874 /// Update prim flags for this group.
2875 /// </summary>
2876 /// <param name="localID"></param>
2877 /// <param name="UsePhysics"></param>
2878 /// <param name="SetTemporary"></param>
2879 /// <param name="SetPhantom"></param>
2880 /// <param name="SetVolumeDetect"></param>
2881 public void UpdatePrimFlags(uint localID, bool UsePhysics, bool SetTemporary, bool SetPhantom, bool SetVolumeDetect)
2882 {
2883 SceneObjectPart selectionPart = GetPart(localID);
2884
2885 if (Scene != null)
2886 {
2887 if (SetTemporary)
2888 {
2889 DetachFromBackup();
2890 // Remove from database and parcel prim count
2891 //
2892 m_scene.DeleteFromStorage(UUID);
2893 }
2894 else if (!Backup)
2895 {
2896 // Previously been temporary now switching back so make it
2897 // available for persisting again
2898 AttachToBackup();
2899 }
2900
2901 m_scene.EventManager.TriggerParcelPrimCountTainted();
2902 }
2903
2904 if (selectionPart != null)
2905 {
2906 SceneObjectPart[] parts = m_parts.GetArray();
2907
2908 if (Scene != null)
2909 {
2910 for (int i = 0; i < parts.Length; i++)
2911 {
2912 SceneObjectPart part = parts[i];
2913 if (part.Scale.X > m_scene.m_maxPhys ||
2914 part.Scale.Y > m_scene.m_maxPhys ||
2915 part.Scale.Z > m_scene.m_maxPhys )
2916 {
2917 UsePhysics = false; // Reset physics
2918 break;
2919 }
2920 }
2921 }
2922
2923 for (int i = 0; i < parts.Length; i++)
2924 parts[i].UpdatePrimFlags(UsePhysics, SetTemporary, SetPhantom, SetVolumeDetect);
2925 }
2926 }
2927
2928 public void UpdateExtraParam(uint localID, ushort type, bool inUse, byte[] data)
2929 {
2930 SceneObjectPart part = GetPart(localID);
2931 if (part != null)
2932 {
2933 part.UpdateExtraParam(type, inUse, data);
2934 }
2935 }
2936
2937 /// <summary>
2938 /// Update the texture entry for this part
2939 /// </summary>
2940 /// <param name="localID"></param>
2941 /// <param name="textureEntry"></param>
2942 public void UpdateTextureEntry(uint localID, byte[] textureEntry)
2943 {
2944 SceneObjectPart part = GetPart(localID);
2945 if (part != null)
2946 {
2947 part.UpdateTextureEntry(textureEntry);
2948 }
2949 }
2950
2951 public void AdjustChildPrimPermissions(bool forceTaskInventoryPermissive)
2952 {
2953 uint newOwnerMask = (uint)(PermissionMask.All | PermissionMask.Export) & 0xfffffff8; // Mask folded bits
2954 uint foldedPerms = RootPart.OwnerMask & 3;
2955
2956 ForEachPart(part =>
2957 {
2958 newOwnerMask &= part.BaseMask;
2959 if (part != RootPart)
2960 part.ClonePermissions(RootPart);
2961 if (forceTaskInventoryPermissive)
2962 part.Inventory.ApplyGodPermissions(part.BaseMask);
2963 });
2964
2965 uint lockMask = ~(uint)(PermissionMask.Move | PermissionMask.Modify);
2966 uint lockBit = RootPart.OwnerMask & (uint)(PermissionMask.Move | PermissionMask.Modify);
2967 RootPart.OwnerMask = (RootPart.OwnerMask & lockBit) | ((newOwnerMask | foldedPerms) & lockMask);
2968
2969// m_log.DebugFormat(
2970// "[SCENE OBJECT GROUP]: RootPart.OwnerMask now {0} for {1} in {2}",
2971// (OpenMetaverse.PermissionMask)RootPart.OwnerMask, Name, Scene.Name);
2972
2973 RootPart.ScheduleFullUpdate();
2974 }
2975
2976 public void UpdatePermissions(UUID AgentID, byte field, uint localID,
2977 uint mask, byte addRemTF)
2978 {
2979 RootPart.UpdatePermissions(AgentID, field, localID, mask, addRemTF);
2980
2981 AdjustChildPrimPermissions(Scene.Permissions.IsGod(AgentID));
2982
2983 HasGroupChanged = true;
2984
2985 // Send the group's properties to all clients once all parts are updated
2986 IClientAPI client;
2987 if (Scene.TryGetClient(AgentID, out client))
2988 SendPropertiesToClient(client);
2989 }
2990
2991 #endregion
2992
2993 #region Shape
2994
2995 /// <summary>
2996 ///
2997 /// </summary>
2998 /// <param name="shapeBlock"></param>
2999 public void UpdateShape(ObjectShapePacket.ObjectDataBlock shapeBlock, uint localID)
3000 {
3001 SceneObjectPart part = GetPart(localID);
3002 if (part != null)
3003 {
3004 part.UpdateShape(shapeBlock);
3005
3006 PhysicsActor pa = m_rootPart.PhysActor;
3007
3008 if (pa != null)
3009 m_scene.PhysicsScene.AddPhysicsActorTaint(pa);
3010 }
3011 }
3012
3013 #endregion
3014
3015 #region Resize
3016
3017 /// <summary>
3018 /// Resize the entire group of prims.
3019 /// </summary>
3020 /// <param name="scale"></param>
3021 public void GroupResize(Vector3 scale)
3022 {
3023// m_log.DebugFormat(
3024// "[SCENE OBJECT GROUP]: Group resizing {0} {1} from {2} to {3}", Name, LocalId, RootPart.Scale, scale);
3025
3026 PhysicsActor pa = m_rootPart.PhysActor;
3027
3028 RootPart.StoreUndoState(true);
3029
3030 if (Scene != null)
3031 {
3032 scale.X = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.X));
3033 scale.Y = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Y));
3034 scale.Z = Math.Max(Scene.m_minNonphys, Math.Min(Scene.m_maxNonphys, scale.Z));
3035
3036 if (pa != null && pa.IsPhysical)
3037 {
3038 scale.X = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.X));
3039 scale.Y = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Y));
3040 scale.Z = Math.Max(Scene.m_minPhys, Math.Min(Scene.m_maxPhys, scale.Z));
3041 }
3042 }
3043
3044 float x = (scale.X / RootPart.Scale.X);
3045 float y = (scale.Y / RootPart.Scale.Y);
3046 float z = (scale.Z / RootPart.Scale.Z);
3047
3048 SceneObjectPart[] parts = m_parts.GetArray();
3049
3050 if (Scene != null & (x > 1.0f || y > 1.0f || z > 1.0f))
3051 {
3052 for (int i = 0; i < parts.Length; i++)
3053 {
3054 SceneObjectPart obPart = parts[i];
3055 if (obPart.UUID != m_rootPart.UUID)
3056 {
3057// obPart.IgnoreUndoUpdate = true;
3058 Vector3 oldSize = new Vector3(obPart.Scale);
3059
3060 float f = 1.0f;
3061 float a = 1.0f;
3062
3063 if (pa != null && pa.IsPhysical)
3064 {
3065 if (oldSize.X * x > Scene.m_maxPhys)
3066 {
3067 f = m_scene.m_maxPhys / oldSize.X;
3068 a = f / x;
3069 x *= a;
3070 y *= a;
3071 z *= a;
3072 }
3073 else if (oldSize.X * x < Scene.m_minPhys)
3074 {
3075 f = m_scene.m_minPhys / oldSize.X;
3076 a = f / x;
3077 x *= a;
3078 y *= a;
3079 z *= a;
3080 }
3081
3082 if (oldSize.Y * y > Scene.m_maxPhys)
3083 {
3084 f = m_scene.m_maxPhys / oldSize.Y;
3085 a = f / y;
3086 x *= a;
3087 y *= a;
3088 z *= a;
3089 }
3090 else if (oldSize.Y * y < Scene.m_minPhys)
3091 {
3092 f = m_scene.m_minPhys / oldSize.Y;
3093 a = f / y;
3094 x *= a;
3095 y *= a;
3096 z *= a;
3097 }
3098
3099 if (oldSize.Z * z > Scene.m_maxPhys)
3100 {
3101 f = m_scene.m_maxPhys / oldSize.Z;
3102 a = f / z;
3103 x *= a;
3104 y *= a;
3105 z *= a;
3106 }
3107 else if (oldSize.Z * z < Scene.m_minPhys)
3108 {
3109 f = m_scene.m_minPhys / oldSize.Z;
3110 a = f / z;
3111 x *= a;
3112 y *= a;
3113 z *= a;
3114 }
3115 }
3116 else
3117 {
3118 if (oldSize.X * x > Scene.m_maxNonphys)
3119 {
3120 f = m_scene.m_maxNonphys / oldSize.X;
3121 a = f / x;
3122 x *= a;
3123 y *= a;
3124 z *= a;
3125 }
3126 else if (oldSize.X * x < Scene.m_minNonphys)
3127 {
3128 f = m_scene.m_minNonphys / oldSize.X;
3129 a = f / x;
3130 x *= a;
3131 y *= a;
3132 z *= a;
3133 }
3134
3135 if (oldSize.Y * y > Scene.m_maxNonphys)
3136 {
3137 f = m_scene.m_maxNonphys / oldSize.Y;
3138 a = f / y;
3139 x *= a;
3140 y *= a;
3141 z *= a;
3142 }
3143 else if (oldSize.Y * y < Scene.m_minNonphys)
3144 {
3145 f = m_scene.m_minNonphys / oldSize.Y;
3146 a = f / y;
3147 x *= a;
3148 y *= a;
3149 z *= a;
3150 }
3151
3152 if (oldSize.Z * z > Scene.m_maxNonphys)
3153 {
3154 f = m_scene.m_maxNonphys / oldSize.Z;
3155 a = f / z;
3156 x *= a;
3157 y *= a;
3158 z *= a;
3159 }
3160 else if (oldSize.Z * z < Scene.m_minNonphys)
3161 {
3162 f = m_scene.m_minNonphys / oldSize.Z;
3163 a = f / z;
3164 x *= a;
3165 y *= a;
3166 z *= a;
3167 }
3168 }
3169
3170// obPart.IgnoreUndoUpdate = false;
3171 }
3172 }
3173 }
3174
3175 Vector3 prevScale = RootPart.Scale;
3176 prevScale.X *= x;
3177 prevScale.Y *= y;
3178 prevScale.Z *= z;
3179
3180// RootPart.IgnoreUndoUpdate = true;
3181 RootPart.Resize(prevScale);
3182// RootPart.IgnoreUndoUpdate = false;
3183
3184 for (int i = 0; i < parts.Length; i++)
3185 {
3186 SceneObjectPart obPart = parts[i];
3187
3188 if (obPart.UUID != m_rootPart.UUID)
3189 {
3190 obPart.IgnoreUndoUpdate = true;
3191
3192 Vector3 currentpos = new Vector3(obPart.OffsetPosition);
3193 currentpos.X *= x;
3194 currentpos.Y *= y;
3195 currentpos.Z *= z;
3196
3197 Vector3 newSize = new Vector3(obPart.Scale);
3198 newSize.X *= x;
3199 newSize.Y *= y;
3200 newSize.Z *= z;
3201
3202 obPart.Resize(newSize);
3203 obPart.UpdateOffSet(currentpos);
3204
3205 obPart.IgnoreUndoUpdate = false;
3206 }
3207
3208// obPart.IgnoreUndoUpdate = false;
3209// obPart.StoreUndoState();
3210 }
3211
3212// m_log.DebugFormat(
3213// "[SCENE OBJECT GROUP]: Finished group resizing {0} {1} to {2}", Name, LocalId, RootPart.Scale);
3214 }
3215
3216 #endregion
3217
3218 #region Position
3219
3220 /// <summary>
3221 /// Move this scene object
3222 /// </summary>
3223 /// <param name="pos"></param>
3224 public void UpdateGroupPosition(Vector3 pos)
3225 {
3226// m_log.DebugFormat("[SCENE OBJECT GROUP]: Updating group position on {0} {1} to {2}", Name, LocalId, pos);
3227
3228 RootPart.StoreUndoState(true);
3229
3230// SceneObjectPart[] parts = m_parts.GetArray();
3231// for (int i = 0; i < parts.Length; i++)
3232// parts[i].StoreUndoState();
3233
3234 if (m_scene.EventManager.TriggerGroupMove(UUID, pos))
3235 {
3236 if (IsAttachment)
3237 {
3238 m_rootPart.AttachedPos = pos;
3239 }
3240
3241 if (RootPart.GetStatusSandbox())
3242 {
3243 if (Util.GetDistanceTo(RootPart.StatusSandboxPos, pos) > 10)
3244 {
3245 RootPart.ScriptSetPhysicsStatus(false);
3246 pos = AbsolutePosition;
3247 Scene.SimChat(Utils.StringToBytes("Hit Sandbox Limit"),
3248 ChatTypeEnum.DebugChannel, 0x7FFFFFFF, RootPart.AbsolutePosition, Name, UUID, false);
3249 }
3250 }
3251
3252 AbsolutePosition = pos;
3253 HasGroupChanged = true;
3254 }
3255
3256 //we need to do a terse update even if the move wasn't allowed
3257 // so that the position is reset in the client (the object snaps back)
3258 RootPart.ScheduleTerseUpdate();
3259 }
3260
3261 /// <summary>
3262 /// Update the position of a single part of this scene object
3263 /// </summary>
3264 /// <param name="pos"></param>
3265 /// <param name="localID"></param>
3266 public void UpdateSinglePosition(Vector3 pos, uint localID)
3267 {
3268 SceneObjectPart part = GetPart(localID);
3269
3270// SceneObjectPart[] parts = m_parts.GetArray();
3271// for (int i = 0; i < parts.Length; i++)
3272// parts[i].StoreUndoState();
3273
3274 if (part != null)
3275 {
3276// m_log.DebugFormat(
3277// "[SCENE OBJECT GROUP]: Updating single position of {0} {1} to {2}", part.Name, part.LocalId, pos);
3278
3279 part.StoreUndoState(false);
3280 part.IgnoreUndoUpdate = true;
3281
3282 if (part.UUID == m_rootPart.UUID)
3283 {
3284 UpdateRootPosition(pos);
3285 }
3286 else
3287 {
3288 part.UpdateOffSet(pos);
3289 }
3290
3291 HasGroupChanged = true;
3292 part.IgnoreUndoUpdate = false;
3293 }
3294 }
3295
3296 /// <summary>
3297 /// Update just the root prim position in a linkset
3298 /// </summary>
3299 /// <param name="newPos"></param>
3300 public void UpdateRootPosition(Vector3 newPos)
3301 {
3302// m_log.DebugFormat(
3303// "[SCENE OBJECT GROUP]: Updating root position of {0} {1} to {2}", Name, LocalId, pos);
3304
3305// SceneObjectPart[] parts = m_parts.GetArray();
3306// for (int i = 0; i < parts.Length; i++)
3307// parts[i].StoreUndoState();
3308
3309 Vector3 oldPos;
3310
3311 if (IsAttachment)
3312 oldPos = m_rootPart.AttachedPos + m_rootPart.OffsetPosition; // OffsetPosition should always be 0 in an attachments's root prim
3313 else
3314 oldPos = AbsolutePosition + m_rootPart.OffsetPosition;
3315
3316 Vector3 diff = oldPos - newPos;
3317 Quaternion partRotation = m_rootPart.RotationOffset;
3318 diff *= Quaternion.Inverse(partRotation);
3319
3320 SceneObjectPart[] parts = m_parts.GetArray();
3321 for (int i = 0; i < parts.Length; i++)
3322 {
3323 SceneObjectPart obPart = parts[i];
3324 if (obPart.UUID != m_rootPart.UUID)
3325 obPart.OffsetPosition = obPart.OffsetPosition + diff;
3326 }
3327
3328 AbsolutePosition = newPos;
3329
3330 if (IsAttachment)
3331 m_rootPart.AttachedPos = newPos;
3332
3333 HasGroupChanged = true;
3334 ScheduleGroupForTerseUpdate();
3335 }
3336
3337 #endregion
3338
3339 #region Rotation
3340
3341 /// <summary>
3342 /// Update the rotation of the group.
3343 /// </summary>
3344 /// <param name="rot"></param>
3345 public void UpdateGroupRotationR(Quaternion rot)
3346 {
3347// m_log.DebugFormat(
3348// "[SCENE OBJECT GROUP]: Updating group rotation R of {0} {1} to {2}", Name, LocalId, rot);
3349
3350// SceneObjectPart[] parts = m_parts.GetArray();
3351// for (int i = 0; i < parts.Length; i++)
3352// parts[i].StoreUndoState();
3353
3354 m_rootPart.StoreUndoState(true);
3355
3356 m_rootPart.UpdateRotation(rot);
3357
3358 PhysicsActor actor = m_rootPart.PhysActor;
3359 if (actor != null)
3360 {
3361 actor.Orientation = m_rootPart.RotationOffset;
3362 m_scene.PhysicsScene.AddPhysicsActorTaint(actor);
3363 }
3364
3365 HasGroupChanged = true;
3366 ScheduleGroupForTerseUpdate();
3367 }
3368
3369 /// <summary>
3370 /// Update the position and rotation of a group simultaneously.
3371 /// </summary>
3372 /// <param name="pos"></param>
3373 /// <param name="rot"></param>
3374 public void UpdateGroupRotationPR(Vector3 pos, Quaternion rot)
3375 {
3376// m_log.DebugFormat(
3377// "[SCENE OBJECT GROUP]: Updating group rotation PR of {0} {1} to {2}", Name, LocalId, rot);
3378
3379// SceneObjectPart[] parts = m_parts.GetArray();
3380// for (int i = 0; i < parts.Length; i++)
3381// parts[i].StoreUndoState();
3382
3383 RootPart.StoreUndoState(true);
3384 RootPart.IgnoreUndoUpdate = true;
3385
3386 m_rootPart.UpdateRotation(rot);
3387
3388 PhysicsActor actor = m_rootPart.PhysActor;
3389 if (actor != null)
3390 {
3391 actor.Orientation = m_rootPart.RotationOffset;
3392 m_scene.PhysicsScene.AddPhysicsActorTaint(actor);
3393 }
3394
3395 if (IsAttachment)
3396 {
3397 m_rootPart.AttachedPos = pos;
3398 }
3399
3400 AbsolutePosition = pos;
3401
3402 HasGroupChanged = true;
3403 ScheduleGroupForTerseUpdate();
3404
3405 RootPart.IgnoreUndoUpdate = false;
3406 }
3407
3408 /// <summary>
3409 /// Update the rotation of a single prim within the group.
3410 /// </summary>
3411 /// <param name="rot"></param>
3412 /// <param name="localID"></param>
3413 public void UpdateSingleRotation(Quaternion rot, uint localID)
3414 {
3415 SceneObjectPart part = GetPart(localID);
3416
3417 SceneObjectPart[] parts = m_parts.GetArray();
3418 for (int i = 0; i < parts.Length; i++)
3419 parts[i].StoreUndoState();
3420
3421 if (part != null)
3422 {
3423// m_log.DebugFormat(
3424// "[SCENE OBJECT GROUP]: Updating single rotation of {0} {1} to {2}", part.Name, part.LocalId, rot);
3425
3426 if (part.UUID == m_rootPart.UUID)
3427 {
3428 UpdateRootRotation(rot);
3429 }
3430 else
3431 {
3432 part.UpdateRotation(rot);
3433 }
3434 }
3435 }
3436
3437 /// <summary>
3438 /// Update the position and rotation simultaneously of a single prim within the group.
3439 /// </summary>
3440 /// <param name="rot"></param>
3441 /// <param name="localID"></param>
3442 public void UpdateSingleRotation(Quaternion rot, Vector3 pos, uint localID)
3443 {
3444 SceneObjectPart part = GetPart(localID);
3445 if (part != null)
3446 {
3447// m_log.DebugFormat(
3448// "[SCENE OBJECT GROUP]: Updating single position and rotation of {0} {1} to {2}",
3449// part.Name, part.LocalId, rot);
3450
3451 part.StoreUndoState();
3452 part.IgnoreUndoUpdate = true;
3453
3454 if (part.UUID == m_rootPart.UUID)
3455 {
3456 UpdateRootRotation(rot);
3457 AbsolutePosition = pos;
3458 }
3459 else
3460 {
3461 part.UpdateRotation(rot);
3462 part.OffsetPosition = pos;
3463 }
3464
3465 part.IgnoreUndoUpdate = false;
3466 }
3467 }
3468
3469 /// <summary>
3470 /// Update the rotation of just the root prim of a linkset.
3471 /// </summary>
3472 /// <param name="rot"></param>
3473 public void UpdateRootRotation(Quaternion rot)
3474 {
3475// m_log.DebugFormat(
3476// "[SCENE OBJECT GROUP]: Updating root rotation of {0} {1} to {2}",
3477// Name, LocalId, rot);
3478
3479 Quaternion axRot = rot;
3480 Quaternion oldParentRot = m_rootPart.RotationOffset;
3481
3482 m_rootPart.StoreUndoState();
3483 m_rootPart.UpdateRotation(rot);
3484
3485 PhysicsActor pa = m_rootPart.PhysActor;
3486
3487 if (pa != null)
3488 {
3489 pa.Orientation = m_rootPart.RotationOffset;
3490 m_scene.PhysicsScene.AddPhysicsActorTaint(pa);
3491 }
3492
3493 SceneObjectPart[] parts = m_parts.GetArray();
3494 for (int i = 0; i < parts.Length; i++)
3495 {
3496 SceneObjectPart prim = parts[i];
3497 if (prim.UUID != m_rootPart.UUID)
3498 {
3499 prim.IgnoreUndoUpdate = true;
3500 Vector3 axPos = prim.OffsetPosition;
3501 axPos *= oldParentRot;
3502 axPos *= Quaternion.Inverse(axRot);
3503 prim.OffsetPosition = axPos;
3504 Quaternion primsRot = prim.RotationOffset;
3505 Quaternion newRot = oldParentRot * primsRot;
3506 newRot = Quaternion.Inverse(axRot) * newRot;
3507 prim.RotationOffset = newRot;
3508 prim.ScheduleTerseUpdate();
3509 prim.IgnoreUndoUpdate = false;
3510 }
3511 }
3512
3513// for (int i = 0; i < parts.Length; i++)
3514// {
3515// SceneObjectPart childpart = parts[i];
3516// if (childpart != m_rootPart)
3517// {
3518//// childpart.IgnoreUndoUpdate = false;
3519//// childpart.StoreUndoState();
3520// }
3521// }
3522
3523 m_rootPart.ScheduleTerseUpdate();
3524
3525// m_log.DebugFormat(
3526// "[SCENE OBJECT GROUP]: Updated root rotation of {0} {1} to {2}",
3527// Name, LocalId, rot);
3528 }
3529
3530 #endregion
3531
3532 internal void SetAxisRotation(int axis, int rotate10)
3533 {
3534 bool setX = false;
3535 bool setY = false;
3536 bool setZ = false;
3537
3538 int xaxis = 2;
3539 int yaxis = 4;
3540 int zaxis = 8;
3541
3542 setX = ((axis & xaxis) != 0) ? true : false;
3543 setY = ((axis & yaxis) != 0) ? true : false;
3544 setZ = ((axis & zaxis) != 0) ? true : false;
3545
3546 float setval = (rotate10 > 0) ? 1f : 0f;
3547
3548 if (setX)
3549 RootPart.RotationAxis.X = setval;
3550 if (setY)
3551 RootPart.RotationAxis.Y = setval;
3552 if (setZ)
3553 RootPart.RotationAxis.Z = setval;
3554
3555 if (setX || setY || setZ)
3556 RootPart.SetPhysicsAxisRotation();
3557 }
3558
3559 public int registerRotTargetWaypoint(Quaternion target, float tolerance)
3560 {
3561 scriptRotTarget waypoint = new scriptRotTarget();
3562 waypoint.targetRot = target;
3563 waypoint.tolerance = tolerance;
3564 uint handle = m_scene.AllocateLocalId();
3565 waypoint.handle = handle;
3566 lock (m_rotTargets)
3567 {
3568 m_rotTargets.Add(handle, waypoint);
3569 }
3570 m_scene.AddGroupTarget(this);
3571 return (int)handle;
3572 }
3573
3574 public void unregisterRotTargetWaypoint(int handle)
3575 {
3576 lock (m_targets)
3577 {
3578 m_rotTargets.Remove((uint)handle);
3579 if (m_targets.Count == 0)
3580 m_scene.RemoveGroupTarget(this);
3581 }
3582 }
3583
3584 public int registerTargetWaypoint(Vector3 target, float tolerance)
3585 {
3586 scriptPosTarget waypoint = new scriptPosTarget();
3587 waypoint.targetPos = target;
3588 waypoint.tolerance = tolerance;
3589 uint handle = m_scene.AllocateLocalId();
3590 waypoint.handle = handle;
3591 lock (m_targets)
3592 {
3593 m_targets.Add(handle, waypoint);
3594 }
3595 m_scene.AddGroupTarget(this);
3596 return (int)handle;
3597 }
3598
3599 public void unregisterTargetWaypoint(int handle)
3600 {
3601 lock (m_targets)
3602 {
3603 m_targets.Remove((uint)handle);
3604 if (m_targets.Count == 0)
3605 m_scene.RemoveGroupTarget(this);
3606 }
3607 }
3608
3609 public void checkAtTargets()
3610 {
3611 if (m_scriptListens_atTarget || m_scriptListens_notAtTarget)
3612 {
3613 if (m_targets.Count > 0)
3614 {
3615 bool at_target = false;
3616 //Vector3 targetPos;
3617 //uint targetHandle;
3618 Dictionary<uint, scriptPosTarget> atTargets = new Dictionary<uint, scriptPosTarget>();
3619 lock (m_targets)
3620 {
3621 foreach (uint idx in m_targets.Keys)
3622 {
3623 scriptPosTarget target = m_targets[idx];
3624 if (Util.GetDistanceTo(target.targetPos, m_rootPart.GroupPosition) <= target.tolerance)
3625 {
3626 // trigger at_target
3627 if (m_scriptListens_atTarget)
3628 {
3629 at_target = true;
3630 scriptPosTarget att = new scriptPosTarget();
3631 att.targetPos = target.targetPos;
3632 att.tolerance = target.tolerance;
3633 att.handle = target.handle;
3634 atTargets.Add(idx, att);
3635 }
3636 }
3637 }
3638 }
3639
3640 if (atTargets.Count > 0)
3641 {
3642 SceneObjectPart[] parts = m_parts.GetArray();
3643 uint[] localids = new uint[parts.Length];
3644 for (int i = 0; i < parts.Length; i++)
3645 localids[i] = parts[i].LocalId;
3646
3647 for (int ctr = 0; ctr < localids.Length; ctr++)
3648 {
3649 foreach (uint target in atTargets.Keys)
3650 {
3651 scriptPosTarget att = atTargets[target];
3652 m_scene.EventManager.TriggerAtTargetEvent(
3653 localids[ctr], att.handle, att.targetPos, m_rootPart.GroupPosition);
3654 }
3655 }
3656
3657 return;
3658 }
3659
3660 if (m_scriptListens_notAtTarget && !at_target)
3661 {
3662 //trigger not_at_target
3663 SceneObjectPart[] parts = m_parts.GetArray();
3664 uint[] localids = new uint[parts.Length];
3665 for (int i = 0; i < parts.Length; i++)
3666 localids[i] = parts[i].LocalId;
3667
3668 for (int ctr = 0; ctr < localids.Length; ctr++)
3669 {
3670 m_scene.EventManager.TriggerNotAtTargetEvent(localids[ctr]);
3671 }
3672 }
3673 }
3674 }
3675 if (m_scriptListens_atRotTarget || m_scriptListens_notAtRotTarget)
3676 {
3677 if (m_rotTargets.Count > 0)
3678 {
3679 bool at_Rottarget = false;
3680 Dictionary<uint, scriptRotTarget> atRotTargets = new Dictionary<uint, scriptRotTarget>();
3681 lock (m_rotTargets)
3682 {
3683 foreach (uint idx in m_rotTargets.Keys)
3684 {
3685 scriptRotTarget target = m_rotTargets[idx];
3686 double angle
3687 = Math.Acos(
3688 target.targetRot.X * m_rootPart.RotationOffset.X
3689 + target.targetRot.Y * m_rootPart.RotationOffset.Y
3690 + target.targetRot.Z * m_rootPart.RotationOffset.Z
3691 + target.targetRot.W * m_rootPart.RotationOffset.W)
3692 * 2;
3693 if (angle < 0) angle = -angle;
3694 if (angle > Math.PI) angle = (Math.PI * 2 - angle);
3695 if (angle <= target.tolerance)
3696 {
3697 // trigger at_rot_target
3698 if (m_scriptListens_atRotTarget)
3699 {
3700 at_Rottarget = true;
3701 scriptRotTarget att = new scriptRotTarget();
3702 att.targetRot = target.targetRot;
3703 att.tolerance = target.tolerance;
3704 att.handle = target.handle;
3705 atRotTargets.Add(idx, att);
3706 }
3707 }
3708 }
3709 }
3710
3711 if (atRotTargets.Count > 0)
3712 {
3713 SceneObjectPart[] parts = m_parts.GetArray();
3714 uint[] localids = new uint[parts.Length];
3715 for (int i = 0; i < parts.Length; i++)
3716 localids[i] = parts[i].LocalId;
3717
3718 for (int ctr = 0; ctr < localids.Length; ctr++)
3719 {
3720 foreach (uint target in atRotTargets.Keys)
3721 {
3722 scriptRotTarget att = atRotTargets[target];
3723 m_scene.EventManager.TriggerAtRotTargetEvent(
3724 localids[ctr], att.handle, att.targetRot, m_rootPart.RotationOffset);
3725 }
3726 }
3727
3728 return;
3729 }
3730
3731 if (m_scriptListens_notAtRotTarget && !at_Rottarget)
3732 {
3733 //trigger not_at_target
3734 SceneObjectPart[] parts = m_parts.GetArray();
3735 uint[] localids = new uint[parts.Length];
3736 for (int i = 0; i < parts.Length; i++)
3737 localids[i] = parts[i].LocalId;
3738
3739 for (int ctr = 0; ctr < localids.Length; ctr++)
3740 {
3741 m_scene.EventManager.TriggerNotAtRotTargetEvent(localids[ctr]);
3742 }
3743 }
3744 }
3745 }
3746 }
3747
3748 public float GetMass()
3749 {
3750 float retmass = 0f;
3751
3752 SceneObjectPart[] parts = m_parts.GetArray();
3753 for (int i = 0; i < parts.Length; i++)
3754 retmass += parts[i].GetMass();
3755
3756 return retmass;
3757 }
3758
3759 /// <summary>
3760 /// If the object is a sculpt/mesh, retrieve the mesh data for each part and reinsert it into each shape so that
3761 /// the physics engine can use it.
3762 /// </summary>
3763 /// <remarks>
3764 /// When the physics engine has finished with it, the sculpt data is discarded to save memory.
3765 /// </remarks>
3766/*
3767 public void CheckSculptAndLoad()
3768 {
3769 if (IsDeleted)
3770 return;
3771
3772 if ((RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Phantom) != 0)
3773 return;
3774
3775// m_log.Debug("Processing CheckSculptAndLoad for {0} {1}", Name, LocalId);
3776
3777 SceneObjectPart[] parts = m_parts.GetArray();
3778
3779 for (int i = 0; i < parts.Length; i++)
3780 parts[i].CheckSculptAndLoad();
3781 }
3782*/
3783 /// <summary>
3784 /// Set the user group to which this scene object belongs.
3785 /// </summary>
3786 /// <param name="GroupID"></param>
3787 /// <param name="client"></param>
3788 public void SetGroup(UUID GroupID, IClientAPI client)
3789 {
3790 SceneObjectPart[] parts = m_parts.GetArray();
3791 for (int i = 0; i < parts.Length; i++)
3792 {
3793 SceneObjectPart part = parts[i];
3794 part.SetGroup(GroupID, client);
3795 part.Inventory.ChangeInventoryGroup(GroupID);
3796 }
3797
3798 HasGroupChanged = true;
3799
3800 // Don't trigger the update here - otherwise some client issues occur when multiple updates are scheduled
3801 // for the same object with very different properties. The caller must schedule the update.
3802 //ScheduleGroupForFullUpdate();
3803 }
3804
3805 public void TriggerScriptChangedEvent(Changed val)
3806 {
3807 SceneObjectPart[] parts = m_parts.GetArray();
3808 for (int i = 0; i < parts.Length; i++)
3809 parts[i].TriggerScriptChangedEvent(val);
3810 }
3811
3812 /// <summary>
3813 /// Returns a count of the number of scripts in this groups parts.
3814 /// </summary>
3815 public int ScriptCount()
3816 {
3817 int count = 0;
3818 SceneObjectPart[] parts = m_parts.GetArray();
3819 for (int i = 0; i < parts.Length; i++)
3820 count += parts[i].Inventory.ScriptCount();
3821
3822 return count;
3823 }
3824
3825 /// <summary>
3826 /// A float the value is a representative execution time in milliseconds of all scripts in the link set.
3827 /// </summary>
3828 public float ScriptExecutionTime()
3829 {
3830 IScriptModule[] engines = Scene.RequestModuleInterfaces<IScriptModule>();
3831
3832 if (engines.Length == 0) // No engine at all
3833 return 0.0f;
3834
3835 float time = 0.0f;
3836
3837 // get all the scripts in all parts
3838 SceneObjectPart[] parts = m_parts.GetArray();
3839 List<TaskInventoryItem> scripts = new List<TaskInventoryItem>();
3840 for (int i = 0; i < parts.Length; i++)
3841 {
3842 scripts.AddRange(parts[i].Inventory.GetInventoryItems(InventoryType.LSL));
3843 }
3844 // extract the UUIDs
3845 List<UUID> ids = new List<UUID>(scripts.Count);
3846 foreach (TaskInventoryItem script in scripts)
3847 {
3848 if (!ids.Contains(script.ItemID))
3849 {
3850 ids.Add(script.ItemID);
3851 }
3852 }
3853 // Offer the list of script UUIDs to each engine found and accumulate the time
3854 foreach (IScriptModule e in engines)
3855 {
3856 if (e != null)
3857 {
3858 time += e.GetScriptExecutionTime(ids);
3859 }
3860 }
3861 return time;
3862 }
3863
3864 /// <summary>
3865 /// Returns a count of the number of running scripts in this groups parts.
3866 /// </summary>
3867 public int RunningScriptCount()
3868 {
3869 int count = 0;
3870 SceneObjectPart[] parts = m_parts.GetArray();
3871 for (int i = 0; i < parts.Length; i++)
3872 count += parts[i].Inventory.RunningScriptCount();
3873
3874 return count;
3875 }
3876
3877 /// <summary>
3878 /// Get a copy of the list of sitting avatars on all prims of this object.
3879 /// </summary>
3880 /// <remarks>
3881 /// This is sorted by the order in which avatars sat down. If an avatar stands up then all avatars that sat
3882 /// down after it move one place down the list.
3883 /// </remarks>
3884 /// <returns>A list of the sitting avatars. Returns an empty list if there are no sitting avatars.</returns>
3885 public List<ScenePresence> GetSittingAvatars()
3886 {
3887 lock (m_sittingAvatars)
3888 return new List<ScenePresence>(m_sittingAvatars);
3889 }
3890
3891 /// <summary>
3892 /// Gets the number of sitting avatars.
3893 /// </summary>
3894 /// <remarks>This applies to all sitting avatars whether there is a sit target set or not.</remarks>
3895 /// <returns></returns>
3896 public int GetSittingAvatarsCount()
3897 {
3898 lock (m_sittingAvatars)
3899 return m_sittingAvatars.Count;
3900 }
3901
3902 public override string ToString()
3903 {
3904 return String.Format("{0} {1} ({2})", Name, UUID, AbsolutePosition);
3905 }
3906
3907 #region ISceneObject
3908
3909 public virtual ISceneObject CloneForNewScene()
3910 {
3911 SceneObjectGroup sog = Copy(false);
3912 sog.IsDeleted = false;
3913 return sog;
3914 }
3915
3916 public virtual string ToXml2()
3917 {
3918 return SceneObjectSerializer.ToXml2Format(this);
3919 }
3920
3921 public virtual string ExtraToXmlString()
3922 {
3923 return "<ExtraFromItemID>" + FromItemID.ToString() + "</ExtraFromItemID>";
3924 }
3925
3926 public virtual void ExtraFromXmlString(string xmlstr)
3927 {
3928 string id = xmlstr.Substring(xmlstr.IndexOf("<ExtraFromItemID>"));
3929 id = xmlstr.Replace("<ExtraFromItemID>", "");
3930 id = id.Replace("</ExtraFromItemID>", "");
3931
3932 UUID uuid = UUID.Zero;
3933 UUID.TryParse(id, out uuid);
3934
3935 FromItemID = uuid;
3936 }
3937
3938 #endregion
3939 }
3940}